/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
Version 0.11.0
*/


/**
* @class The superclass of all menu containers.
* @constructor
* @extends YAHOO.widget.Overlay
* @base YAHOO.widget.Overlay
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a MenuModule instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.MenuModule = function(p_oElement, p_oConfig) {

    YAHOO.widget.MenuModule.superclass.constructor.call(
        this, 
        p_oElement, 
        p_oConfig
    );

};

YAHOO.extend(YAHOO.widget.MenuModule, YAHOO.widget.Overlay);


// Constants

/**
* Constant representing the CSS class(es) to be applied to the root 
* HTMLDivElement of the MenuModule instance.
* @final
* @type String
*/
YAHOO.widget.MenuModule.prototype.CSS_CLASS_NAME = "yuimenu";


/**
* Constant representing the type of item to instantiate and add when parsing 
* the child nodes (either HTMLLIElement, HTMLOptGroupElement or 
* HTMLOptionElement) of a menu's DOM.  The default 
* is YAHOO.widget.MenuModuleItem.
* @final
* @type YAHOO.widget.MenuModuleItem
*/
YAHOO.widget.MenuModule.prototype.ITEM_TYPE = null;


/**
* Constant representing the tagname of the HTMLElement used to title 
* a group of items.
* @final
* @type String
*/
YAHOO.widget.MenuModule.prototype.GROUP_TITLE_TAG_NAME = "H6";


// Private properties

/**
* Array of HTMLElements used to title groups of items.
* @private
* @type {Array}
*/
YAHOO.widget.MenuModule.prototype._aGroupTitleElements = null;


/**
* Multi-dimensional array of items.
* @private
* @type {Array}
*/
YAHOO.widget.MenuModule.prototype._aItemGroups = null;


/**
* An array of HTMLUListElements, each of which is the parent node of each 
* items's HTMLLIElement node.
* @private
* @type {Array}
*/
YAHOO.widget.MenuModule.prototype._aListElements = null;


/**
* Reference to the Event utility singleton.
* @private
* @type {YAHOO.util.Event}
*/
YAHOO.widget.MenuModule.prototype._oEventUtil = YAHOO.util.Event;


/**
* Reference to the Dom utility singleton.
* @private
* @type {YAHOO.util.Dom}
*/
YAHOO.widget.MenuModule.prototype._oDom = YAHOO.util.Dom;


/**
* Reference to the item the mouse is currently over.
* @private
* @type {YAHOO.widget.MenuModuleItem}
*/
YAHOO.widget.MenuModule.prototype._oCurrentItem = null;


/** 
* The current state of a MenuModule instance's "mouseover" event
* @private
* @type {Boolean}
*/
YAHOO.widget.MenuModule.prototype._bFiredMouseOverEvent = false;


/** 
* The current state of a MenuModule instance's "mouseout" event
* @private
* @type {Boolean}
*/
YAHOO.widget.MenuModule.prototype._bFiredMouseOutEvent = false;


// Public properties

/**
* Reference to the item that has focus.
* @private
* @type {YAHOO.widget.MenuModuleItem}
*/
YAHOO.widget.MenuModule.prototype.activeItem = null;


/**
* Returns a MenuModule instance's parent object.
* @type {YAHOO.widget.MenuModuleItem}
*/
YAHOO.widget.MenuModule.prototype.parent = null;


/**
* Returns the HTMLElement (either HTMLSelectElement or HTMLDivElement)
* used create the MenuModule instance.
* @type {HTMLSelectElement/HTMLDivElement}
*/
YAHOO.widget.MenuModule.prototype.srcElement = null;


// Events

/**
* Fires when the mouse has entered a MenuModule instance.  Passes back the 
* DOM Event object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.mouseOverEvent = null;


/**
* Fires when the mouse has left a MenuModule instance.  Passes back the DOM 
* Event object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.mouseOutEvent = null;


/**
* Fires when the user mouses down on a MenuModule instance.  Passes back the 
* DOM Event object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.mouseDownEvent = null;


/**
* Fires when the user releases a mouse button while the mouse is over 
* a MenuModule instance.  Passes back the DOM Event object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.mouseUpEvent = null;


/**
* Fires when the user clicks the on a MenuModule instance.  Passes back the 
* DOM Event object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.clickEvent = null;


/**
* Fires when the user presses an alphanumeric key.  Passes back the 
* DOM Event object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.keyPressEvent = null;


/**
* Fires when the user presses a key.  Passes back the DOM Event 
* object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.keyDownEvent = null;


/**
* Fires when the user releases a key.  Passes back the DOM Event 
* object as an argument.
* @type {YAHOO.util.CustomEvent}
* @see YAHOO.util.CustomEvent
*/
YAHOO.widget.MenuModule.prototype.keyUpEvent = null;


/**
* The MenuModule class's initialization method. This method is automatically 
* called  by the constructor, and sets up all DOM references for 
* pre-existing markup, and creates required markup if it is not already present.
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a MenuModule instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.MenuModule.prototype.init = function(p_oElement, p_oConfig) {

    var Dom = this._oDom;
    var Event = this._oEventUtil;


    if(!this.ITEM_TYPE) {

        this.ITEM_TYPE = YAHOO.widget.MenuModuleItem;

    }


    this._aItemGroups = [];
    this._aListElements = [];
    this._aGroupTitleElements = [];


    var oElement;

    if(typeof p_oElement == "string") {

        oElement = document.getElementById(p_oElement);

    }
    else if(p_oElement.tagName) {

        oElement = p_oElement;

    }


    if(oElement) {

        switch(oElement.tagName) {
    
            case "DIV":

                this.srcElement = oElement;

                /* 
                    Note: we don't pass the user config in here yet 
                    because we only want it executed once, at the lowest 
                    subclass level.
                */ 
            
                YAHOO.widget.MenuModule.superclass.init.call(this, oElement);

                this.beforeInitEvent.fire(YAHOO.widget.MenuModule);


                /*
                    Populate the collection of item groups and item
                    group titles
                */

                var oNode = this.body.firstChild;
                var i = 0;

                do {

                    switch(oNode.tagName) {

                        case this.GROUP_TITLE_TAG_NAME:
                        
                            this._aGroupTitleElements[i] = oNode;

                        break;

                        case "UL":

                            this._aListElements[i] = oNode;
                            this._aItemGroups[i] = [];
                            i++;

                        break;

                    }

                }
                while((oNode = oNode.nextSibling));


                /*
                    Apply the "first-of-type" class to the first UL to mimic 
                    the "first-of-type" CSS3 psuedo class.
                */

                if(this._aListElements[0]) {

                    Dom.addClass(this._aListElements[0], "first-of-type");

                }

                this.logger = new YAHOO.widget.LogWriter(this.toString());

                this.logger.log("Source element: " + this.srcElement.tagName);
    
            break;
    
            case "SELECT":
    
                this.srcElement = oElement;
    
    
                /*
                    The source element is not something that we can use 
                    outright, so we need to create a new Overlay
                */
    
                var sId = Dom.generateId();

                /* 
                    Note: we don't pass the user config in here yet 
                    because we only want it executed once, at the lowest 
                    subclass level.
                */ 
            
                YAHOO.widget.MenuModule.superclass.init.call(this, sId); 

                this.beforeInitEvent.fire(YAHOO.widget.MenuModule);

                this.logger = new YAHOO.widget.LogWriter(this.toString());

                this.logger.log("Source element: " + this.srcElement.tagName);

            break;
    
        }

    }
    else {

        /* 
            Note: we don't pass the user config in here yet 
            because we only want it executed once, at the lowest 
            subclass level.
        */ 
    
        YAHOO.widget.MenuModule.superclass.init.call(this, p_oElement);

        this.beforeInitEvent.fire(YAHOO.widget.MenuModule);

        this.logger = new YAHOO.widget.LogWriter(this.toString());

        this.logger.log("No source element found.  " +
            "Created element with id: " + this.id);

    }


    if(this.element) {

        var oEl = this.element;
        var CustomEvent = YAHOO.util.CustomEvent;

        Dom.addClass(oEl, this.CSS_CLASS_NAME);

        // Assign DOM event handlers

        Event.addListener(
                oEl, 
                "mouseover", 
                this._onElementMouseOver, 
                this, 
                true
            );

        Event.addListener(oEl, "mouseout", this._onElementMouseOut, this, true);
        Event.addListener(oEl, "mousedown", this._onDOMEvent, this, true);
        Event.addListener(oEl, "mouseup", this._onDOMEvent, this, true);
        Event.addListener(oEl, "click", this._onElementClick, this, true);
        Event.addListener(oEl, "keydown", this._onDOMEvent, this, true);
        Event.addListener(oEl, "keyup", this._onDOMEvent, this, true);
        Event.addListener(oEl, "keypress", this._onDOMEvent, this, true);


        // Create custom events

        this.mouseOverEvent = new CustomEvent("mouseOverEvent", this);
        this.mouseOutEvent = new CustomEvent("mouseOutEvent", this);
        this.mouseDownEvent = new CustomEvent("mouseDownEvent", this);
        this.mouseUpEvent = new CustomEvent("mouseUpEvent", this);
        this.clickEvent = new CustomEvent("clickEvent", this);
        this.keyPressEvent = new CustomEvent("keyPressEvent", this);
        this.keyDownEvent = new CustomEvent("keyDownEvent", this);
        this.keyUpEvent = new CustomEvent("keyUpEvent", this);


        // Subscribe to Custom Events

        this.beforeRenderEvent.subscribe(this._onBeforeRender, this, true);
        this.renderEvent.subscribe(this._onRender, this, true);
        this.showEvent.subscribe(this._onShow, this, true);
        this.beforeHideEvent.subscribe(this._onBeforeHide, this, true);


        if(p_oConfig) {
    
            this.cfg.applyConfig(p_oConfig, true);
    
        }


        this.cfg.queueProperty("visible", false);


        if(this.srcElement) {

            this._initSubTree();

        }

    }


    this.initEvent.fire(YAHOO.widget.MenuModule);

};


// Private methods

/**
* Iterates the source element's childNodes collection and uses the child 
* nodes to instantiate MenuModule and MenuModuleItem instances.
* @private
*/
YAHOO.widget.MenuModule.prototype._initSubTree = function() {

    var oNode;

    this.logger.log("Searching DOM for items to initialize.");

    switch(this.srcElement.tagName) {

        case "DIV":

            if(this._aListElements.length > 0) {

                this.logger.log("Found " + 
                    this._aListElements.length + " item groups to initialize.");

                var i = this._aListElements.length - 1;

                do {

                    oNode = this._aListElements[i].firstChild;
    
                    this.logger.log("Scanning " + 
                        this._aListElements[i].childNodes.length + 
                        " child nodes for items to initialize.");

                    do {
    
                        switch(oNode.tagName) {
        
                            case "LI":

                                this.logger.log("Initializing " + 
                                    oNode.tagName + " node.");

                                this.addItem(new this.ITEM_TYPE(oNode), i);
        
                            break;
        
                        }
            
                    }
                    while((oNode = oNode.nextSibling));
            
                }
                while(i--);

            }

        break;

        case "SELECT":

            this.logger.log("Scanning " +  this.srcElement.childNodes.length + 
                " child nodes for items to initialize.");

            oNode = this.srcElement.firstChild;

            do {

                switch(oNode.tagName) {

                    case "OPTGROUP":
                    case "OPTION":

                        this.logger.log("Initializing " +  
                            oNode.tagName + " node.");

                        this.addItem(new this.ITEM_TYPE(oNode));

                    break;

                }

            }
            while((oNode = oNode.nextSibling));

        break;

    }

};


/**
* Returns the first enabled item in a menu instance.
* @return Returns a MenuModuleItem instance.
* @type YAHOO.widget.MenuModuleItem
* @private
*/
YAHOO.widget.MenuModule.prototype._getFirstEnabledItem = function() {

    var nGroups = this._aItemGroups.length;
    var oItem;
    var aItemGroup;

    for(var i=0; i<nGroups; i++) {

        aItemGroup = this._aItemGroups[i];
        
        if(aItemGroup) {

            var nItems = aItemGroup.length;
            
            for(var n=0; n<nItems; n++) {
            
                oItem = aItemGroup[n];
                
                if(!oItem.cfg.getProperty("disabled")) {
                
                    return oItem;
                
                }
    
                oItem = null;
    
            }
        
        }
    
    }
    
};


/**
* Determines if the value is one of the supported positions.
* @private
* @param {Object} p_sPosition The object to be evaluated.
* @return Returns true if the position is supported.
* @type Boolean
*/
YAHOO.widget.MenuModule.prototype._checkPosition = function(p_sPosition) {

    if(typeof p_sPosition == "string") {

        var sPosition = p_sPosition.toLowerCase();

        return ("dynamic,static".indexOf(sPosition) != -1);

    }

};


/**
* Adds an item to a group.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which
* the item belongs.
* @param {YAHOO.widget.MenuModuleItem} p_oItem The item to be added.
* @param {Number} p_nItemIndex Optional. Index at which the item 
* should be added.
* @return The item that was added.
* @type YAHOO.widget.MenuModuleItem
*/
YAHOO.widget.MenuModule.prototype._addItemToGroup = 

    function(p_nGroupIndex, p_oItem, p_nItemIndex) {

        var Dom = this._oDom;
        var oItem;

        if(p_oItem instanceof this.ITEM_TYPE) {

            oItem = p_oItem;     

        }
        else if(typeof p_oItem == "string") {

            oItem = new this.ITEM_TYPE(p_oItem);
        
        }


        if(oItem) {
        
            var nGroupIndex = typeof p_nGroupIndex == "number" ? 
                    p_nGroupIndex : 0;
            
            var aGroup = this._getItemGroup(nGroupIndex);
            
            var oGroupItem;
    

            if(!aGroup) {
    
                aGroup = this._createItemGroup(nGroupIndex);
    
            }


            if(typeof p_nItemIndex == "number") {
    
                var bAppend = (p_nItemIndex >= aGroup.length);            
    

                if(aGroup[p_nItemIndex]) {
        
                    aGroup.splice(p_nItemIndex, 0, oItem);
        
                }
                else {
        
                    aGroup[p_nItemIndex] = oItem;
        
                }
    
    
                oGroupItem = aGroup[p_nItemIndex];
    
                if(oGroupItem) {
    
                    if(bAppend && !oGroupItem.element.parentNode) {
            
                        this._aListElements[nGroupIndex].appendChild(
                            oGroupItem.element
                        );
        
                    }
                    else {
      
        
                        /**
                        * Returns the next sibling of an item in an array 
                        * @param {p_aArray} An array
                        * @param {p_nStartIndex} The index to start searching
                        * the array 
                        * @ignore
                        * @return Returns an item in an array
                        * @type Object 
                        */
                        function getNextItemSibling(p_aArray, p_nStartIndex) {
                
                            return (
                                    p_aArray[p_nStartIndex] || 
                                    getNextItemSibling(
                                        p_aArray, 
                                        (p_nStartIndex+1)
                                    )
                                );
                
                        }
        
        
                        var oNextItemSibling = 
                                getNextItemSibling(aGroup, (p_nItemIndex+1));
        
                        if(oNextItemSibling && !oGroupItem.element.parentNode) {
                
                            this._aListElements[nGroupIndex].insertBefore(
                                    oGroupItem.element, 
                                    oNextItemSibling.element
                                );
            
                        }
        
                    }
        
    
                    oGroupItem.parent = this;
            
                    this._subscribeToItemEvents(oGroupItem);
        
                    this._configureItemSubmenuModule(oGroupItem);
                    
                    this._updateItemProperties(nGroupIndex);
            
                    this.logger.log("Item inserted." + 
                        " Text: " + oGroupItem.cfg.getProperty("text") + ", " + 
                        " Index: " + oGroupItem.index + ", " + 
                        " Group Index: " + oGroupItem.groupIndex);

                    return oGroupItem;
        
                }
    
            }
            else {
        
                var nItemIndex = aGroup.length;
        
                aGroup[nItemIndex] = oItem;
        
        
                oGroupItem = aGroup[nItemIndex];
        
                if(oGroupItem) {
        
                    if(
                        !Dom.isAncestor(
                            this._aListElements[nGroupIndex], 
                            oGroupItem.element
                        )
                    ) {
        
                        this._aListElements[nGroupIndex].appendChild(
                            oGroupItem.element
                        );
        
                    }
        
                    oGroupItem.element.setAttribute("groupindex", nGroupIndex);
                    oGroupItem.element.setAttribute("index", nItemIndex);
            
                    oGroupItem.parent = this;
        
                    oGroupItem.index = nItemIndex;
                    oGroupItem.groupIndex = nGroupIndex;
            
                    this._subscribeToItemEvents(oGroupItem);
        
                    this._configureItemSubmenuModule(oGroupItem);
        
                    if(nItemIndex === 0) {
            
                        Dom.addClass(oGroupItem.element, "first-of-type");
            
                    }

                    this.logger.log("Item added." + 
                        " Text: " + oGroupItem.cfg.getProperty("text") + ", " + 
                        " Index: " + oGroupItem.index + ", " + 
                        " Group Index: " + oGroupItem.groupIndex);
            
                    return oGroupItem;
        
                }
        
            }

        }
    
    };


/**
* Removes an item from a group by index.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which
* the item belongs.
* @param {Number} p_nItemIndex Number indicating the index of the item to  
* be removed.
* @return The item that was removed.
* @type YAHOO.widget.MenuModuleItem
*/    
YAHOO.widget.MenuModule.prototype._removeItemFromGroupByIndex = 

    function(p_nGroupIndex, p_nItemIndex) {

        var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0;
        var aGroup = this._getItemGroup(nGroupIndex);
    
        if(aGroup) {
    
            var aArray = aGroup.splice(p_nItemIndex, 1);
            var oItem = aArray[0];
        
            if(oItem) {
        
                // Update the index and className properties of each member        
                
                this._updateItemProperties(nGroupIndex);
        
                if(aGroup.length === 0) {
        
                    // Remove the UL
        
                    var oUL = this._aListElements[nGroupIndex];
        
                    if(this.body && oUL) {
        
                        this.body.removeChild(oUL);
        
                    }
        
                    // Remove the group from the array of items
        
                    this._aItemGroups.splice(nGroupIndex, 1);
        
        
                    // Remove the UL from the array of ULs
        
                    this._aListElements.splice(nGroupIndex, 1);
        
        
                    /*
                         Assign the "first-of-type" class to the new first UL 
                         in the collection
                    */
        
                    oUL = this._aListElements[0];
        
                    if(oUL) {
        
                        this._oDom.addClass(oUL, "first-of-type");
        
                    }            
        
                }
        
        
                // Return a reference to the item that was removed
            
                return oItem;
        
            }
    
        }
    
    };


/**
* Removes a item from a group by reference.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which
* the item belongs.
* @param {YAHOO.widget.MenuModuleItem} p_oItem The item to be removed.
* @return The item that was removed.
* @type YAHOO.widget.MenuModuleItem
*/    
YAHOO.widget.MenuModule.prototype._removeItemFromGroupByValue =

    function(p_nGroupIndex, p_oItem) {

        var aGroup = this._getItemGroup(p_nGroupIndex);

        if(aGroup) {

            var nItems = aGroup.length;
            var nItemIndex = -1;
        
            if(nItems > 0) {
        
                var i = nItems-1;
            
                do {
            
                    if(aGroup[i] == p_oItem) {
            
                        nItemIndex = i;
                        break;    
            
                    }
            
                }
                while(i--);
            
                if(nItemIndex > -1) {
            
                    return this._removeItemFromGroupByIndex(
                                p_nGroupIndex, 
                                nItemIndex
                            );
            
                }
        
            }
        
        }
    
    };


/**
* Updates the index, groupindex, and className properties of the items
* in the specified group. 
* @private
* @param {Number} p_nGroupIndex Number indicating the group of items to update.
*/
YAHOO.widget.MenuModule.prototype._updateItemProperties = 

    function(p_nGroupIndex) {

        var aGroup = this._getItemGroup(p_nGroupIndex);
        var nItems = aGroup.length;
    
        if(nItems > 0) {
    
            var Dom = this._oDom;
            var i = nItems - 1;
            var oItem;
            var oLI;
    
            // Update the index and className properties of each member        
        
            do {
    
                oItem = aGroup[i];
    
                if(oItem) {
        
                    oLI = oItem.element;
    
                    oItem.index = i;
                    oItem.groupIndex = p_nGroupIndex;
    
                    oLI.setAttribute("groupindex", p_nGroupIndex);
                    oLI.setAttribute("index", i);
    
                    Dom.removeClass(oLI, "first-of-type");
    
                }
        
            }
            while(i--);
    
    
            if(oLI) {
    
                Dom.addClass(oLI, "first-of-type");
    
            }
    
        }
    
    };


/**
* Creates a new item group (array) and it's associated HTMLUlElement node 
* @private
* @param {Number} p_nIndex Number indicating the group to create.
* @return An item group.
* @type Array
*/
YAHOO.widget.MenuModule.prototype._createItemGroup = function(p_nIndex) {

    if(!this._aItemGroups[p_nIndex]) {

        this._aItemGroups[p_nIndex] = [];

        var oUL = document.createElement("ul");

        this._aListElements[p_nIndex] = oUL;

        return this._aItemGroups[p_nIndex];

    }

};


/**
* Returns the item group at the specified index.
* @private
* @param {Number} p_nIndex Number indicating the index of the item group to
* be retrieved.
* @return An array of items.
* @type Array
*/
YAHOO.widget.MenuModule.prototype._getItemGroup = function(p_nIndex) {

    var nIndex = ((typeof p_nIndex == "number") ? p_nIndex : 0);

    return this._aItemGroups[nIndex];

};


/**
* Subscribe's a MenuModule instance to it's parent MenuModule instance's events.
* @private
* @param {YAHOO.widget.MenuModuleItem} p_oItem The item to listen
* for events on.
*/
YAHOO.widget.MenuModule.prototype._configureItemSubmenuModule = 

    function(p_oItem) {

        var oSubmenu = p_oItem.cfg.getProperty("submenu");
    
        if(oSubmenu) {
    
            /*
                Listen for configuration changes to the parent MenuModule 
                instance so they they can be applied to the submenu.
            */
    
            this.cfg.configChangedEvent.subscribe(
                this._onParentMenuModuleConfigChange, 
                oSubmenu, 
                true
            );
            
            this.renderEvent.subscribe(
                this._onParentMenuModuleRender,
                oSubmenu, 
                true
            );
    
            oSubmenu.beforeShowEvent.subscribe(
                this._onSubmenuBeforeShow, 
                oSubmenu, 
                true
            );
    
            oSubmenu.showEvent.subscribe(this._onSubmenuShow, oSubmenu, true);
    
            oSubmenu.hideEvent.subscribe(this._onSubmenuHide, oSubmenu, true);
    
        }

};


/**
* Subscribes a MenuModule instance to the specified item's Custom Events.
* @private
* @param {YAHOO.widget.MenuModuleItem} p_oItem The item to listen for events on.
*/
YAHOO.widget.MenuModule.prototype._subscribeToItemEvents = function(p_oItem) {

    var aArguments = [this, p_oItem];

    p_oItem.focusEvent.subscribe(this._onItemFocus, aArguments);

    p_oItem.blurEvent.subscribe(this._onItemBlur, aArguments);

    p_oItem.cfg.configChangedEvent.subscribe(
        this._onItemConfigChange,
        aArguments
    );

};


/**
* Returns the offset width of a MenuModule instance.
* @private
*/
YAHOO.widget.MenuModule.prototype._getOffsetWidth = function() {

    var oClone = this.element.cloneNode(true);

    this._oDom.setStyle(oClone, "width", "");

    document.body.appendChild(oClone);

    var sWidth = oClone.offsetWidth;

    document.body.removeChild(oClone);

    return sWidth;

};


/**
* Determines if a DOM event was fired on an item and (if so) fires the item's
* associated Custom Event
* @private
* @param {HTMLElement} p_oElement The original target of the event.
* @param {String} p_sEventType The type/name of the Custom Event to fire.
* @param {Event} p_oDOMEvent The DOM event to pass back when firing the 
* Custom Event.
* @return An item.
* @type YAHOO.widget.MenuModuleItem
*/
YAHOO.widget.MenuModule.prototype._fireItemEvent = 

    function(p_oElement, p_sEventType, p_oDOMEvent) {

        var me = this;
    
        /**
        * Returns the specified element's parent HTMLLIElement (&#60;LI&#60;)
        * @param {p_oElement} An HTMLElement node
        * @ignore
        * @return Returns an HTMLElement node
        * @type HTMLElement 
        */
        function getItemElement(p_oElement) {
        
            if(p_oElement == me.element) {
    
                return;
            
            }
            else if(p_oElement.tagName == "LI") {
        
                return p_oElement;
        
            }
            else if(p_oElement.parentNode) {
    
                return getItemElement(p_oElement.parentNode);
        
            }
        
        }
    
    
        var oElement = getItemElement(p_oElement);
    
        if(oElement) {
    
            /*
                Retrieve the item that corresponds to the 
                HTMLLIElement (&#60;LI&#60;) and fire the Custom Event        
            */
    
            var nGroupIndex = parseInt(oElement.getAttribute("groupindex"), 10);
            var nIndex = parseInt(oElement.getAttribute("index"), 10);
            var oItem = this._aItemGroups[nGroupIndex][nIndex];
    
            if(!oItem.cfg.getProperty("disabled")) {
    
                oItem[p_sEventType].fire(p_oDOMEvent);
    
                return oItem;
    
            }
    
        }

    };


// Private DOM event handlers

/**
* Generic event handler for the MenuModule's root HTMLDivElement node.  Used 
* to handle "mousedown," "mouseup," "keydown," "keyup," and "keypress" events.
* @private
* @param {Event} p_oEvent Event object passed back by the event 
* utility (YAHOO.util.Event).
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance 
* corresponding to the HTMLDivElement that fired the event.
*/
YAHOO.widget.MenuModule.prototype._onDOMEvent = 

    function(p_oEvent, p_oMenuModule) {

        var Event = this._oEventUtil;

        // Map of DOM event types to Custom Event types

        var oEventTypes =  {
                "mousedown": "mouseDownEvent",
                "mouseup": "mouseUpEvent",
                "keydown": "keyDownEvent",
                "keyup": "keyUpEvent",
                "keypress": "keyPressEvent"
            };
    
        var sCustomEventType = oEventTypes[p_oEvent.type];
        
        var oTarget = Event.getTarget(p_oEvent);
    
        /*
            Check if the target was an element that is a part of a 
            an item and (if so), fire the associated custom event.
        */
    
        this._fireItemEvent(oTarget, sCustomEventType, p_oEvent);

    
        // Fire the associated custom event for the MenuModule
    
        this[sCustomEventType].fire(p_oEvent);
    
    
        /*
            Stop the propagation of the event at each MenuModule instance
            since menus can be embedded in eachother.
        */
            
        Event.stopPropagation(p_oEvent);

    };


/**
* "mouseover" event handler for the MenuModule's root HTMLDivElement node.
* @private
* @param {Event} p_oEvent Event object passed back by the event
* utility (YAHOO.util.Event).
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance 
* corresponding to the HTMLDivElement that fired the event.
*/
YAHOO.widget.MenuModule.prototype._onElementMouseOver = 

    function(p_oEvent, p_oMenuModule) {

        var Event = this._oEventUtil;
        var oTarget = Event.getTarget(p_oEvent);
    
        if(
            (
                oTarget == this.element || 
                this._oDom.isAncestor(this.element, oTarget)
            )  && 
            !this._bFiredMouseOverEvent
        ) {
    
            // Fire the "mouseover" Custom Event for the MenuModule instance
    
            this.mouseOverEvent.fire(p_oEvent);
    
            this._bFiredMouseOverEvent = true;
            this._bFiredMouseOutEvent = false;
    
        }
    
    
        /*
            Check if the target was an element that is a part of an item
            and (if so), fire the "mouseover" Custom Event.
        */
    
        if(!this._oCurrentItem) {
    
            this._oCurrentItem = 
                this._fireItemEvent(oTarget, "mouseOverEvent", p_oEvent);
    
        }
    
    
        /*
            Stop the propagation of the event at each MenuModule instance
            since menus can be embedded in eachother.
        */
    
        Event.stopPropagation(p_oEvent);

    };


/**
* "mouseout" event handler for the MenuModule's root HTMLDivElement node.
* @private
* @param {Event} p_oEvent Event object passed back by the event
* utility (YAHOO.util.Event).
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance 
* corresponding to the HTMLDivElement that fired the event.
*/
YAHOO.widget.MenuModule.prototype._onElementMouseOut = 

    function(p_oEvent, p_oMenuModule) {

        var Dom = this._oDom;
        var Event = this._oEventUtil;
        var oRelatedTarget = Event.getRelatedTarget(p_oEvent);
        var bLIMouseOut = true;
        var bMovingToSubmenu = false;
            
    
        // Determine where the mouse is going
    
        if(this._oCurrentItem && oRelatedTarget) {
    
            if(
                oRelatedTarget == this._oCurrentItem.element || 
                Dom.isAncestor(this._oCurrentItem.element, oRelatedTarget)
            ) {
    
                bLIMouseOut = false;
    
            }
    
    
            var oSubmenu = this._oCurrentItem.cfg.getProperty("submenu");
    
            if(
                oSubmenu && 
                (
                    oRelatedTarget == oSubmenu.element ||
                    Dom.isAncestor(oSubmenu.element, oRelatedTarget)
                )
            ) {
    
                bMovingToSubmenu = true;
    
            }
    
        }
    
    
        if(this._oCurrentItem && (bLIMouseOut || bMovingToSubmenu)) {
    
            // Fire the "mouseout" Custom Event for the item
    
            this._oCurrentItem.mouseOutEvent.fire(p_oEvent);
    
            this._oCurrentItem = null;
    
        }
    
    
        if(
            !this._bFiredMouseOutEvent && 
            (
                !Dom.isAncestor(this.element, oRelatedTarget) ||
                bMovingToSubmenu
            )
        ) {
    
            // Fire the "mouseout" Custom Event for the MenuModule instance
    
            this.mouseOutEvent.fire(p_oEvent);
    
            this._bFiredMouseOutEvent = true;
            this._bFiredMouseOverEvent = false;
    
        }
    
    
        /*
            Stop the propagation of the event at each MenuModule instance
            since menus can be embedded in eachother.
        */
    
        Event.stopPropagation(p_oEvent);

    };


/**
* "click" event handler for the MenuModule's root HTMLDivElement node.
* @private
* @param {Event} p_oEvent Event object passed back by the 
* event utility (YAHOO.util.Event).
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance 
* corresponding to the HTMLDivElement that fired the event.
*/         
YAHOO.widget.MenuModule.prototype._onElementClick = 

    function(p_oEvent, p_oMenuModule) {

        var Event = this._oEventUtil;
        
        var oTarget = Event.getTarget(p_oEvent);
        
        /*
            Check if the target was a DOM element that is a part of an
            item and (if so), fire the associated "click" 
            Custom Event.
        */
        
        var oItem = this._fireItemEvent(oTarget, "clickEvent", p_oEvent);
        
        var bCurrentPageURL; // Indicates if the URL points to the current page
    
    
        if(oItem) {
    
            var sURL = oItem.cfg.getProperty("url");
            var oSubmenu = oItem.cfg.getProperty("submenu");
            
            bCurrentPageURL = (sURL.substr((sURL.length-1),1) == "#");
    
            /*
                ACCESSIBILITY FEATURE FOR SCREEN READERS: Expand/collapse the
                submenu when the user clicks on the submenu indicator image.
            */        
    
            if(oTarget == oItem.submenuIndicator && oSubmenu) {

                if(oSubmenu.cfg.getProperty("visible")) {
        
                    oSubmenu.hide();
        
                }
                else {

                    var oActiveItem = this.activeItem;
               

                    // Hide any other submenus that might be visible
                
                    if(oActiveItem && oActiveItem != this) {
                
                        this.clearActiveItem();
                
                    }

                    this.activeItem = oItem;
        
                    oItem.cfg.setProperty("selected", true);

                    oSubmenu.show();
        
                }
        
            }
            else if(oTarget.tagName != "A" && !bCurrentPageURL) {
                
                /*
                    Follow the URL of the item regardless of whether or 
                    not the user clicked specifically on the
                    HTMLAnchorElement (&#60;A&#60;) node.
                */
    
                document.location = sURL;
        
            }
        
        }
            
    
        switch(oTarget.tagName) {
        
            case "A":
            
                if(bCurrentPageURL) {
    
                    // Don't follow URLs that are equal to "#"
    
                    Event.preventDefault(p_oEvent);
                
                }
                else {
    
                    /*
                        Break if the anchor's URL is something other than "#" 
                        to prevent the call to "stopPropagation" from be 
                        executed.  This is required for Safari to be able to 
                        follow the URL.
                    */
                
                    break;
                
                }
            
            default:
    
                /*
                    Stop the propagation of the event at each MenuModule 
                    instance since Menus can be embedded in eachother.
                */
    
                Event.stopPropagation(p_oEvent);
            
            break;
        
        }
    
    
        // Fire the associated "click" Custom Event for the MenuModule instance
    
        this.clickEvent.fire(p_oEvent);

    };


// Private Custom Event handlers


/**
* "beforerender" Custom Event handler for a MenuModule instance.  Appends all 
* of the HTMLUListElement (&#60;UL&#60;s) nodes (and their child 
* HTMLLIElement (&#60;LI&#60;)) nodes and their accompanying title nodes to  
* the body of the MenuModule instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuModule.prototype._onBeforeRender = 

    function(p_sType, p_aArgs, p_oMenuModule) {

        var Dom = this._oDom;
        var oConfig = this.cfg;
        var oEl = this.element;
        var nListElements = this._aListElements.length;
    

        if(oConfig.getProperty("position") == "static") {
    
            oConfig.queueProperty("iframe", false);
            oConfig.queueProperty("visible", true);
            
        }
    
    
        if(nListElements > 0) {
    
            var i = 0;
            var bFirstList = true;
            var oUL;
            var oGroupTitle;
    
    
            do {
    
                oUL = this._aListElements[i];
    
                if(oUL) {
    
                    if(bFirstList) {
            
                        Dom.addClass(oUL, "first-of-type");
                        bFirstList = false;
            
                    }
    
    
                    if(!Dom.isAncestor(oEl, oUL)) {
    
                        this.appendToBody(oUL);
    
                    }
    
    
                    oGroupTitle = this._aGroupTitleElements[i];
    
                    if(oGroupTitle) {
    
                        if(!Dom.isAncestor(oEl, oGroupTitle)) {
    
                            oUL.parentNode.insertBefore(oGroupTitle, oUL);
    
                        }
    
    
                        Dom.addClass(oUL, "hastitle");
    
                    }
    
                }
    
                i++;
    
            }
            while(i < nListElements);
    
        }

    };


/**
* "render" Custom Event handler for a MenuModule instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuModule.prototype._onRender = 

    function(p_sType, p_aArgs, p_oMenuModule) {

        if(this.cfg.getProperty("position") == "dynamic") {
    
            var sWidth = this.element.parentNode.tagName == "BODY" ? 
                    this.element.offsetWidth : this._getOffsetWidth();
        
            this.cfg.setProperty("width", (sWidth + "px"));
    
        }

    };


/**
* "show" Custom Event handler for a MenuModule instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuModule.prototype._onShow = 

    function(p_sType, p_aArgs, p_oMenuModule) {
    
        /*
            Setting focus to an item in the newly visible submenu alerts the 
            contents of the submenu to the screen reader.
        */

        this.setInitialFocus();
    
    };


/**
* "hide" Custom Event handler for a MenuModule instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuModule.prototype._onBeforeHide = 

    function(p_sType, p_aArgs, p_oMenuModule) {

        var oActiveItem = this.activeItem;

        if(oActiveItem) {
    
            oActiveItem.blur();

            if(oActiveItem.cfg.getProperty("selected")) {
    
                oActiveItem.cfg.setProperty("selected", false);
    
            }
    
            var oSubmenu = oActiveItem.cfg.getProperty("submenu");
    
            if(oSubmenu && oSubmenu.cfg.getProperty("visible")) {
    
                oSubmenu.hide();
    
            }
    
        }

    };


/**
* "configchange" Custom Event handler for a submenu.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oSubmenu The submenu that subscribed
* to the event.
*/
YAHOO.widget.MenuModule.prototype._onParentMenuModuleConfigChange = 

    function(p_sType, p_aArgs, p_oSubmenu) {
    
        var sPropertyName = p_aArgs[0][0];
        var oPropertyValue = p_aArgs[0][1];
    
        switch(sPropertyName) {
    
            case "iframe":
            case "constraintoviewport":
    
                p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
                    
            break;        
            
        }
    
    };


/**
* "render" Custom Event handler for a MenuModule instance.  Renders a  
* submenu in response to the firing of it's parent's "render" event.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oSubmenu The submenu that subscribed
* to the event.
*/
YAHOO.widget.MenuModule.prototype._onParentMenuModuleRender = 

    function(p_sType, p_aArgs, p_oSubmenu) {

        /*
            Set the "iframe" and "constraintoviewport" configuration 
            properties to match the parent MenuModule
        */ 
    
        var oParentMenu = p_oSubmenu.parent.parent;
    
        p_oSubmenu.cfg.applyConfig(
        
            {
                constraintoviewport: 
                    oParentMenu.cfg.getProperty("constraintoviewport"),
    
                xy: [0,0],
    
                iframe: oParentMenu.cfg.getProperty("iframe")
    
            }
        
        );
    
    
        if(this._oDom.inDocument(this.element)) {
    
            this.render();
    
        }
        else {
    
            this.render(this.parent.element);
    
        }
    
    };


/**
* "beforeshow" Custom Event handler for a submenu.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oSubmenu The submenu that fired
* the event.
*/
YAHOO.widget.MenuModule.prototype._onSubmenuBeforeShow = 

    function(p_sType, p_aArgs, p_oSubmenu) {
    
        var oParent = this.parent;
        var aAlignment = oParent.parent.cfg.getProperty("submenualignment");

        this.cfg.setProperty(
            "context", 
            [
                oParent.element, 
                aAlignment[0], 
                aAlignment[1]
            ]
        );

        oParent.submenuIndicator.alt = 
            oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT;
    
    };


/**
* "show" Custom Event handler for a submenu.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oSubmenu The submenu that fired
* the event.
*/
YAHOO.widget.MenuModule.prototype._onSubmenuShow = 

    function(p_sType, p_aArgs, p_oSubmenu) {
    
        var oParent = this.parent;

        oParent.submenuIndicator.alt = 
            oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT;
    
    };


/**
* "hide" Custom Event handler for a submenu.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oSubmenu The submenu that fired
* the event.
*/
YAHOO.widget.MenuModule.prototype._onSubmenuHide = 

    function(p_sType, p_aArgs, p_oSubmenu) {
    
        var oParent = this.parent;

        if(oParent.parent.cfg.getProperty("visible")) {

            oParent.cfg.setProperty("selected", false);
    
            oParent.focus();
        
        }

        oParent.submenuIndicator.alt = 
            oParent.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;
    
    };


/**
* "focus" YAHOO.util.CustomEvent handler for a MenuModule instance's items.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {Array} p_aObjects Array containing the current MenuModule instance 
* and the item that fired the event.
*/
YAHOO.widget.MenuModule.prototype._onItemFocus = 

    function(p_sType, p_aArgs, p_aObjects) {
    
        var me = p_aObjects[0];
        var oItem = p_aObjects[1];
    
        me.activeItem = oItem;
    
    };


/**
* "blur" YAHOO.util.CustomEvent handler for a MenuModule instance's items.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {Array} p_aObjects Array containing the current MenuModule instance 
* and the item that fired the event.
*/
YAHOO.widget.MenuModule.prototype._onItemBlur = 

    function(p_sType, p_aArgs, p_aObjects) {
    
        var me = p_aObjects[0];
        var oItem = p_aObjects[1];
        var oSubmenu = oItem.cfg.getProperty("submenu");
    
        if(!oSubmenu || (oSubmenu && !oSubmenu.cfg.getProperty("visible"))) {
    
            me.activeItem = null;
    
        }
    
    };


/**
* "configchange" YAHOO.util.CustomEvent handler for the MenuModule 
* instance's items.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the 
* event was fired.
* @param {Array} p_aObjects Array containing the current MenuModule instance 
* and the item that fired the event.
*/
YAHOO.widget.MenuModule.prototype._onItemConfigChange = 

    function(p_sType, p_aArgs, p_aObjects) {

        var me = p_aObjects[0];    
        var sProperty = p_aArgs[0][0];
        var oItem = p_aObjects[1];
    
        switch(sProperty) {
    
            case "submenu":
    
                var oSubmenu = p_aArgs[0][1];
    
                if(oSubmenu) {
    
                    me._configureItemSubmenuModule(oItem);
    
                }
    
            break;
    
            case "text":
            case "helptext":
    
                /*
                    A change to an item's "text" or "helptext"
                    configuration properties requires the width of the parent
                    MenuModule instance to be recalculated.
                */
    
                if(me.element.style.width) {
        
                    var sWidth = me._getOffsetWidth() + "px";
    
                    me._oDom.setStyle(me.element, "width", sWidth);
    
                }
    
            break;
    
        }
    
    };


/**
* The default event handler executed when the moveEvent is fired, if the 
* "constraintoviewport" configuration property is set to true.
*/
YAHOO.widget.MenuModule.prototype.enforceConstraints = 

    function(type, args, obj) {

        var Dom = this._oDom;
        var oConfig = this.cfg;
    
        var pos = args[0];
            
        var x = pos[0];
        var y = pos[1];
        
        var bod = document.getElementsByTagName('body')[0];
        var htm = document.getElementsByTagName('html')[0];
        
        var bodyOverflow = Dom.getStyle(bod, "overflow");
        var htmOverflow = Dom.getStyle(htm, "overflow");
        
        var offsetHeight = this.element.offsetHeight;
        var offsetWidth = this.element.offsetWidth;
        
        var viewPortWidth = Dom.getClientWidth();
        var viewPortHeight = Dom.getClientHeight();
        
        var scrollX = window.scrollX || document.body.scrollLeft;
        var scrollY = window.scrollY || document.body.scrollTop;
        
        var topConstraint = scrollY + 10;
        var leftConstraint = scrollX + 10;
        var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10;
        var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10;
        
        var aContext = oConfig.getProperty("context");
        var oContextElement = aContext ? aContext[0] : null;
    
    
        if (x < 10) {
    
            x = leftConstraint;
    
        } else if ((x + offsetWidth) > viewPortWidth) {
    
            if(
                oContextElement && 
                ((x - oContextElement.offsetWidth) > offsetWidth)
            ) {
    
                x = (x - (oContextElement.offsetWidth + offsetWidth));
    
            }
            else {
    
                x = rightConstraint;
    
            }
    
        }
    
        if (y < 10) {
    
            y = topConstraint;
    
        } else if (y > bottomConstraint) {
    
            if(oContextElement && (y > offsetHeight)) {
    
                y = ((y + oContextElement.offsetHeight) - offsetHeight);
    
            }
            else {
    
                y = bottomConstraint;
    
            }
    
        }
    
        oConfig.setProperty("x", x, true);
        oConfig.setProperty("y", y, true);
    
    };


// Event handlers for configuration properties

/**
* Event handler for when the "position" configuration property of a
* MenuModule changes.
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance fired
* the event.
* @see YAHOO.widget.Overlay#configIframe
*/
YAHOO.widget.MenuModule.prototype.configPosition = 

    function(p_sType, p_aArgs, p_oMenuModule) {

        var sCSSPosition = p_aArgs[0] == "static" ? "static" : "absolute";
    
        this._oDom.setStyle(this.element, "position", sCSSPosition);
    
    };


// Public methods

YAHOO.widget.MenuModule.prototype.toString = function() {

    return ("Menu " + this.id);

};


/**
* Sets the title of a group of items.
* @param {String} p_sGroupTitle The title of the group.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to which
* the title belongs.
*/
YAHOO.widget.MenuModule.prototype.setItemGroupTitle = 

    function(p_sGroupTitle, p_nGroupIndex) {
        
        if(typeof p_sGroupTitle == "string" && p_sGroupTitle.length > 0) {
    
            var Dom = this._oDom;

            var nGroupIndex = 
                    typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0;
    
            var oTitle = this._aGroupTitleElements[nGroupIndex];
    
    
            if(oTitle) {
    
                oTitle.innerHTML = p_sGroupTitle;
                
            }
            else {
    
                oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME);
                        
                oTitle.innerHTML = p_sGroupTitle;
    
                this._aGroupTitleElements[nGroupIndex] = oTitle;
    
            }
    
    
            var i = this._aGroupTitleElements.length - 1;
            var nFirstIndex;
    
            do {
    
                if(this._aGroupTitleElements[i]) {
    
                    Dom.removeClass(
                        this._aGroupTitleElements[i],
                        "first-of-type"
                    );

                    nFirstIndex = i;
    
                }
    
            }
            while(i--);
    
    
            if(nFirstIndex !== null) {
    
                Dom.addClass(
                    this._aGroupTitleElements[nFirstIndex], 
                    "first-of-type"
                );
    
            }
    
        }
    
    };


/**
* Appends the specified item to a MenuModule instance.
* @param {YAHOO.widget.MenuModuleItem} p_oItem The item to be added.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to which
* the item belongs.
* @return The item that was added to the MenuModule.
* @type YAHOO.widget.MenuModuleItem
*/
YAHOO.widget.MenuModule.prototype.addItem = function(p_oItem, p_nGroupIndex) {

    if(p_oItem) {

        return this._addItemToGroup(p_nGroupIndex, p_oItem);
        
    }

};


/**
* Inserts an item into a MenuModule instance at the specified index.
* @param {YAHOO.widget.MenuModuleItem} p_oItem The item to be inserted.
* @param {Number} p_nItemIndex Number indicating the ordinal position 
* at which the item should be added.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to which
* the item belongs.
* @return The item that was inserted into the MenuModule.
* @type YAHOO.widget.MenuModuleItem
*/
YAHOO.widget.MenuModule.prototype.insertItem = 

    function(p_oItem, p_nItemIndex, p_nGroupIndex) {
    
        if(p_oItem) {
    
            return this._addItemToGroup(p_nGroupIndex, p_oItem, p_nItemIndex);
    
        }
    
    };


/**
* Removes the specified item from a MenuModule instance.
* @param {YAHOO.widget.MenuModuleItem/Number} p_oObject The item or index of 
* the item to be removed.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to which
* the item belongs.
* @return The item that was removed from the MenuModule.
* @type YAHOO.widget.MenuModuleItem
*/
YAHOO.widget.MenuModule.prototype.removeItem =

    function(p_oObject, p_nGroupIndex) {
    
        if(typeof p_oObject != "undefined") {
    
            var oItem;
    
            if(p_oObject instanceof YAHOO.widget.MenuModuleItem) {
    
                oItem = 
                    this._removeItemFromGroupByValue(p_nGroupIndex, p_oObject);           
    
            }
            else if(typeof p_oObject == "number") {
    
                oItem = 
                    this._removeItemFromGroupByIndex(p_nGroupIndex, p_oObject);
    
            }
    
            if(oItem) {
    
                oItem.destroy();

                this.logger.log("Item removed." + 
                    " Text: " + oItem.cfg.getProperty("text") + ", " + 
                    " Index: " + oItem.index + ", " + 
                    " Group Index: " + oItem.groupIndex);
    
                return oItem;
    
            }
    
        }
    
    };


/**
* Returns a multi-dimensional array of all of a MenuModule's items.
* @return An array of items.
* @type Array
*/        
YAHOO.widget.MenuModule.prototype.getItemGroups = function() {

    return this._aItemGroups;

};


/**
* Returns the item at the specified index.
* @param {Number} p_nItemIndex Number indicating the ordinal position of the 
* item to be retrieved.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to which
* the item belongs.
* @return An item.
* @type YAHOO.widget.MenuModuleItem
*/
YAHOO.widget.MenuModule.prototype.getItem = 

    function(p_nItemIndex, p_nGroupIndex) {
    
        if(typeof p_nItemIndex == "number") {
    
            var aGroup = this._getItemGroup(p_nGroupIndex);
    
            if(aGroup) {
    
                return aGroup[p_nItemIndex];
            
            }
    
        }
    
    };


/**
* Removes the MenuModule instance's element from the DOM and sets all child 
* elements to null.
*/
YAHOO.widget.MenuModule.prototype.destroy = function() {

    // Remove DOM event handlers

    this._oEventUtil.purgeElement(this.element);


    // Remove Custom Event listeners

    this.mouseOverEvent.unsubscribeAll();
    this.mouseOutEvent.unsubscribeAll();
    this.mouseDownEvent.unsubscribeAll();
    this.mouseUpEvent.unsubscribeAll();
    this.clickEvent.unsubscribeAll();
    this.keyPressEvent.unsubscribeAll();
    this.keyDownEvent.unsubscribeAll();
    this.keyUpEvent.unsubscribeAll();
    this.beforeMoveEvent.unsubscribeAll();


    var nItemGroups = this._aItemGroups.length;
    var nItems;
    var oItemGroup;
    var oItem;
    var i;
    var n;


    // Remove all items

    if(nItemGroups > 0) {

        i = nItemGroups - 1;

        do {

            oItemGroup = this._aItemGroups[i];

            if(oItemGroup) {

                nItems = oItemGroup.length;
    
                if(nItems > 0) {
    
                    n = nItems - 1;
        
                    do {

                        oItem = this._aItemGroups[i][n];

                        if(oItem) {
        
                            oItem.destroy();
                        }
        
                    }
                    while(n--);
    
                }

            }

        }
        while(i--);

    }        


    // Continue with the superclass implementation of this method

    YAHOO.widget.MenuModule.superclass.destroy.call(this);
    
    this.logger.log("Destroyed.");

};




/**
* Sets focus to a MenuModule instance's first enabled item.
*/
YAHOO.widget.MenuModule.prototype.setInitialFocus = function() {

    var oItem = this._getFirstEnabledItem();
    
    if(oItem) {
    
        oItem.focus();
    }
    
};


/**
* Sets the "selected" configuration property of a MenuModule instance's first
* enabled item to "true."
*/
YAHOO.widget.MenuModule.prototype.setInitialSelection = function() {

    var oItem = this._getFirstEnabledItem();
    
    if(oItem) {
    
        oItem.cfg.setProperty("selected", true);
    }        

};


/**
* Sets the "selected" configuration property of a MenuModule instance's active 
* item to "false," blurs the item and hide's the item's submenu.
*/
YAHOO.widget.MenuModule.prototype.clearActiveItem = function () {

    if(this.activeItem) {

        var oConfig = this.activeItem.cfg;

        oConfig.setProperty("selected", false);

        var oSubmenu = oConfig.getProperty("submenu");

        if(oSubmenu) {

            oSubmenu.hide();

        }

    }

};


/**
* Initializes the class's configurable properties which can be changed using 
* the MenuModule's Config object (cfg).
*/
YAHOO.widget.MenuModule.prototype.initDefaultConfig = function() {

    YAHOO.widget.MenuModule.superclass.initDefaultConfig.call(this);

    var oConfig = this.cfg;

	// Add configuration properties

    oConfig.addProperty(
        "position", 
        {
            value: "dynamic", 
            handler: this.configPosition, 
            validator: this._checkPosition 
        } 
    );

    oConfig.refireEvent("position");

    oConfig.addProperty("submenualignment", { value: ["tl","tr"] } );

};



/**
* @class The MenuModuleItem class allows you to create and modify an item for a
* MenuModule instance.
* @constructor
* @param {String or HTMLElement} p_oObject String or HTMLElement 
* (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
* source HTMLElement node.
* @param {Object} p_oConfig The configuration object literal containing 
* the configuration for a MenuModuleItem instance. See the configuration 
* class documentation for more details.
*/
YAHOO.widget.MenuModuleItem = function(p_oObject, p_oConfig) {

    if(p_oObject) {

        this.init(p_oObject, p_oConfig);

    }

};

YAHOO.widget.MenuModuleItem.prototype = {

    // Constants

    /**
    * Constant representing the path to the image to be used for the submenu
    * arrow indicator.
    * @final
    * @type String
    */
    SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarorght8_nrm_1.gif",


    /**
    * Constant representing the path to the image to be used for the submenu
    * arrow indicator when a MenuModuleItem instance is selected.
    * @final
    * @type String
    */
    SELECTED_SUBMENU_INDICATOR_IMAGE_PATH: 
        "nt/ic/ut/alt1/menuarorght8_hov_1.gif",


    /**
    * Constant representing the path to the image to be used for the submenu
    * arrow indicator when a MenuModuleItem instance is disabled.
    * @final
    * @type String
    */
    DISABLED_SUBMENU_INDICATOR_IMAGE_PATH: 
        "nt/ic/ut/alt1/menuarorght8_dim_1.gif",


    /**
    * Constant representing the alt text for the image to be used for the 
    * submenu arrow indicator.
    * @final
    * @type String
    */
    COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT: "Collapsed.  Click to expand.",


    /**
    * Constant representing the alt text for the image to be used for the 
    * submenu arrow indicator when the submenu is visible.
    * @final
    * @type String
    */
    EXPANDED_SUBMENU_INDICATOR_ALT_TEXT: "Expanded.  Click to collapse.",


    /**
    * Constant representing the alt text for the image to be used for the 
    * submenu arrow indicator when a MenuModuleItem instance is disabled.
    * @final
    * @type String
    */
    DISABLED_SUBMENU_INDICATOR_ALT_TEXT: "Disabled.",


    /**
    * Constant representing the CSS class(es) to be applied to the root 
    * HTMLLIElement of the MenuModuleItem.
    * @final
    * @type String
    */
    CSS_CLASS_NAME: "yuimenuitem",


    /**
    * Constant representing the type of menu to instantiate when creating 
    * submenu instances from parsing the child nodes (either HTMLSelectElement 
    * or HTMLDivElement) of the item's DOM.  The default 
    * is YAHOO.widget.MenuModule.
    * @final
    * @type YAHOO.widget.MenuModule
    */
    SUBMENU_TYPE: null,


    /**
    * Constant representing the type of item to instantiate when 
    * creating item instances from parsing the child nodes (either 
    * HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
    * submenu's DOM.  
    * The default is YAHOO.widget.MenuModuleItem.
    * @final
    * @type YAHOO.widget.MenuModuleItem
    */
    SUBMENU_ITEM_TYPE: null,


    /**
    * Constant representing the prefix path to use for non-secure images
    * @type string
    */
    IMG_ROOT: "http://us.i1.yimg.com/us.yimg.com/i/",
    

    /**
    * Constant representing the prefix path to use for securely served images
    * @type string
    */
    IMG_ROOT_SSL: "https://a248.e.akamai.net/sec.yimg.com/i/",


    // Private member variables
    
    /**
    * Reference to the HTMLAnchorElement of the MenuModuleItem's core internal
    * DOM structure.
    * @private
    * @type {HTMLAnchorElement}
    */
    _oAnchor: null,
    

    /**
    * Reference to the text node of the MenuModuleItem's core internal
    * DOM structure.
    * @private
    * @type {Text}
    */
    _oText: null,
    
    
    /**
    * Reference to the HTMLElement (&#60;EM&#60;) used to create the optional
    * help text for a MenuModuleItem instance.
    * @private
    * @type {HTMLElement}
    */
    _oHelpTextEM: null,
    
    
    /**
    * Reference to the submenu for a MenuModuleItem instance.
    * @private
    * @type {YAHOO.widget.MenuModule}
    */
    _oSubmenu: null,
    
    
    /**
    * Reference to the Dom utility singleton.
    * @private
    * @type {YAHOO.util.Dom}
    */
    _oDom: YAHOO.util.Dom,


    // Public properties

	/**
	* The class's constructor function
	* @type YAHOO.widget.MenuModuleItem
	*/
	constructor: YAHOO.widget.MenuModuleItem,


	/**
	* The string representing the image root
	* @type string
	*/
	imageRoot: null,


	/**
	* Boolean representing whether or not the current browsing context 
	* is secure (https)
	* @type boolean
	*/
	isSecure: YAHOO.widget.Module.prototype.isSecure,


    /**
    * Returns the ordinal position of a MenuModuleItem instance in a group.
    * @type Number
    */
    index: null,


    /**
    * Returns the index of the group to which a MenuModuleItem instance belongs.
    * @type Number
    */
    groupIndex: null,


    /**
    * Returns the parent object for a MenuModuleItem instance.
    * @type {YAHOO.widget.MenuModule}
    */
    parent: null,


    /**
    * Returns the HTMLLIElement for a MenuModuleItem instance.
    * @type {HTMLLIElement}
    */
    element: null,


    /**
    * Returns the HTMLElement (either HTMLLIElement, HTMLOptGroupElement or
    * HTMLOptionElement) used create the MenuModuleItem instance.
    * @type {HTMLLIElement/HTMLOptGroupElement/HTMLOptionElement}
    */
    srcElement: null,


    /**
    * Specifies an arbitrary value for a MenuModuleItem instance.
    * @type {Object}
    */
    value: null,


    /**
    * Reference to the HTMLImageElement used to create the submenu
    * indicator for a MenuModuleItem instance.
    * @type {HTMLImageElement}
    */
    submenuIndicator: null,


	/**
	* String representing the browser
	* @type string
	*/
	browser: YAHOO.widget.Module.prototype.browser,


    // Events

    /**
    * Fires when a MenuModuleItem instances's HTMLLIElement is removed from
    * it's parent HTMLUListElement node.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    destroyEvent: null,


    /**
    * Fires when the mouse has entered a MenuModuleItem instance.  Passes
    * back the DOM Event object as an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    mouseOverEvent: null,


    /**
    * Fires when the mouse has left a MenuModuleItem instance.  Passes back  
    * the DOM Event object as an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    mouseOutEvent: null,


    /**
    * Fires when the user mouses down on a MenuModuleItem instance.  Passes 
    * back the DOM Event object as an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    mouseDownEvent: null,


    /**
    * Fires when the user releases a mouse button while the mouse is 
    * over a MenuModuleItem instance.  Passes back the DOM Event object as
    * an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    mouseUpEvent: null,


    /**
    * Fires when the user clicks the on a MenuModuleItem instance.  Passes 
    * back the DOM Event object as an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    clickEvent: null,


    /**
    * Fires when the user presses an alphanumeric key.  Passes back the 
    * DOM Event object as an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    keyPressEvent: null,


    /**
    * Fires when the user presses a key.  Passes back the DOM Event 
    * object as an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    keyDownEvent: null,


    /**
    * Fires when the user releases a key.  Passes back the DOM Event 
    * object as an argument.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    keyUpEvent: null,


    /**
    * Fires when a MenuModuleItem instance receives focus.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    focusEvent: null,


    /**
    * Fires when a MenuModuleItem instance loses the input focus.
    * @type {YAHOO.util.CustomEvent}
    * @see YAHOO.util.CustomEvent
    */
    blurEvent: null,


    /**
    * The MenuModuleItem class's initialization method. This method is 
    * automatically called by the constructor, and sets up all DOM references 
    * for pre-existing markup, and creates required markup if it is not
    * already present.
    * @param {String or HTMLElement} p_oObject String or HTMLElement 
    * (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
    * source HTMLElement node.
    * @param {Object} p_oConfig The configuration object literal containing 
    * the configuration for a MenuModuleItem instance. See the configuration 
    * class documentation for more details.
    */
    init: function(p_oObject, p_oConfig) {

        this.imageRoot = (this.isSecure) ? this.IMG_ROOT_SSL : this.IMG_ROOT;


        if(!this.SUBMENU_TYPE) {
    
            this.SUBMENU_TYPE = YAHOO.widget.MenuModule;
    
        }

        if(!this.SUBMENU_ITEM_TYPE) {
    
            this.SUBMENU_ITEM_TYPE = YAHOO.widget.MenuModuleItem;
    
        }


        // Create the config object

        this.cfg = new YAHOO.util.Config(this);

        this.initDefaultConfig();

        var oConfig = this.cfg;


        if(this._checkString(p_oObject)) {

            this._createRootNodeStructure();

            oConfig.setProperty("text", p_oObject);

        }
        else if(this._checkDOMNode(p_oObject)) {

            switch(p_oObject.tagName) {

                case "OPTION":

                    this._createRootNodeStructure();

                    oConfig.setProperty("text", p_oObject.text);

                    this.srcElement = p_oObject;

                break;

                case "OPTGROUP":

                    this._createRootNodeStructure();

                    oConfig.setProperty("text", p_oObject.label);

                    this.srcElement = p_oObject;

                    this._initSubTree();

                break;

                case "LI":

                    // Get the anchor node (if it exists)

                    var oAnchor = this._getFirstElement(p_oObject, "A");
                    var sURL = "#";
                    var sText = null;


                    // Capture the "text" and/or the "URL"

                    if(oAnchor) {

                        sURL = oAnchor.getAttribute("href");

                        if(oAnchor.innerText) {
                
                            sText = oAnchor.innerText;
                
                        }
                        else {
                
                            var oRange = oAnchor.ownerDocument.createRange();
                
                            oRange.selectNodeContents(oAnchor);
                
                            sText = oRange.toString();             
                
                        }

                    }
                    else {

                        var oText = p_oObject.firstChild;

                        sText = oText.nodeValue;

                        oAnchor = document.createElement("a");
                        
                        oAnchor.setAttribute("href", sURL);

                        p_oObject.replaceChild(oAnchor, oText);
                        
                        oAnchor.appendChild(oText);

                    }


                    this.srcElement = p_oObject;
                    this.element = p_oObject;
                    this._oAnchor = oAnchor;
    

                    // Check if emphasis has been applied to the MenuModuleItem

                    var oEmphasisNode = this._getFirstElement(oAnchor);
                    var bEmphasis = false;
                    var bStrongEmphasis = false;

                    if(oEmphasisNode) {

                        // Set a reference to the text node 

                        this._oText = oEmphasisNode.firstChild;

                        switch(oEmphasisNode.tagName) {

                            case "EM":

                                bEmphasis = true;

                            break;

                            case "STRONG":

                                bStrongEmphasis = true;

                            break;

                        }

                    }
                    else {

                        // Set a reference to the text node 

                        this._oText = oAnchor.firstChild;

                    }


                    /*
                        Set these properties silently to sync up the 
                        configuration object without making changes to the 
                        element's DOM
                    */ 

                    oConfig.setProperty("text", sText, true);
                    oConfig.setProperty("url", sURL, true);
                    oConfig.setProperty("emphasis", bEmphasis, true);
                    oConfig.setProperty(
                        "strongemphasis", 
                        bStrongEmphasis, 
                        true
                    );

                    this._initSubTree();

                break;

            }            

        }


        if(this.element) {


            this._oDom.addClass(this.element, this.CSS_CLASS_NAME);


            // Create custom events
    
            var CustomEvent = YAHOO.util.CustomEvent;
    
            this.destroyEvent = new CustomEvent("destroyEvent", this);
            this.mouseOverEvent = new CustomEvent("mouseOverEvent", this);
            this.mouseOutEvent = new CustomEvent("mouseOutEvent", this);
            this.mouseDownEvent = new CustomEvent("mouseDownEvent", this);
            this.mouseUpEvent = new CustomEvent("mouseUpEvent", this);
            this.clickEvent = new CustomEvent("clickEvent", this);
            this.keyPressEvent = new CustomEvent("keyPressEvent", this);
            this.keyDownEvent = new CustomEvent("keyDownEvent", this);
            this.keyUpEvent = new CustomEvent("keyUpEvent", this);
            this.focusEvent = new CustomEvent("focusEvent", this);
            this.blurEvent = new CustomEvent("blurEvent", this);


            if(p_oConfig) {
    
                oConfig.applyConfig(p_oConfig);
    
            }        

            oConfig.fireQueue();

        }

    },


    // Private methods

    /**
    * Returns an HTMLElement's first HTMLElement node
    * @private
    * @param {HTMLElement} p_oElement The element to be evaluated.
    * @param {String} p_sTagName Optional. The tagname of the element.
    * @return Returns an HTMLElement node.
    * @type Boolean
    */
    _getFirstElement: function(p_oElement, p_sTagName) {

        var oElement;

        if(p_oElement.firstChild && p_oElement.firstChild.nodeType == 1) {

            oElement = p_oElement.firstChild;

        }
        else if(
            p_oElement.firstChild && 
            p_oElement.firstChild.nextSibling && 
            p_oElement.firstChild.nextSibling.nodeType == 1
        ) {

            oElement = p_oElement.firstChild.nextSibling;

        }


        if(p_sTagName) {

            return (oElement && oElement.tagName == p_sTagName) ? 
                oElement : false;

        }

        return oElement;

    },


    /**
    * Determines if an object is a string
    * @private
    * @param {Object} p_oObject The object to be evaluated.
    * @return Returns true if the object is a string.
    * @type Boolean
    */
    _checkString: function(p_oObject) {

        return (typeof p_oObject == "string");

    },


    /**
    * Determines if an object is an HTMLElement.
    * @private
    * @param {Object} p_oObject The object to be evaluated.
    * @return Returns true if the object is an HTMLElement.
    * @type Boolean
    */
    _checkDOMNode: function(p_oObject) {

        return (p_oObject && p_oObject.tagName);

    },


    /**
    * Creates the core DOM structure for a MenuModuleItem instance.
    * @private
    */
    _createRootNodeStructure: function () {

        this.element = document.createElement("li");

        this._oText = document.createTextNode("");

        this._oAnchor = document.createElement("a");
        this._oAnchor.appendChild(this._oText);
        
        this.cfg.refireEvent("url");

        this.element.appendChild(this._oAnchor);            

    },


    /**
    * Iterates the source element's childNodes collection and uses the  
    * child nodes to instantiate other menus.
    * @private
    */
    _initSubTree: function() {

        var Menu = this.SUBMENU_TYPE;
        var MenuModuleItem = this.SUBMENU_ITEM_TYPE;
        var oSrcEl = this.srcElement;
        var oConfig = this.cfg;


        if(oSrcEl.childNodes.length > 0) {

            var oNode = oSrcEl.firstChild;
            var aOptions = [];

            do {

                switch(oNode.tagName) {
        
                    case "DIV":
        
                        oConfig.setProperty("submenu", (new Menu(oNode)));
        
                    break;
 
                    case "OPTION":

                        aOptions[aOptions.length] = oNode;

                    break;
       
                }
            
            }        
            while((oNode = oNode.nextSibling));


            var nOptions = aOptions.length;

            if(nOptions > 0) {
    
                oConfig.setProperty(
                    "submenu", 
                    (new Menu(this._oDom.generateId()))
                );
    
                for(var n=0; n<nOptions; n++) {
    
                    this._oSubmenu.addItem((new MenuModuleItem(aOptions[n])));
    
                }
    
            }

        }

    },


    // Event handlers for configuration properties

    /**
    * Event handler for when the "text" configuration property of
    * a MenuModuleItem instance changes. 
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */
    configText: function(p_sType, p_aArgs, p_oItem) {

        var sText = p_aArgs[0];


        if(this._oText) {

            this._oText.nodeValue = sText;

        }

    },


    /**
    * Event handler for when the "helptext" configuration property of
    * a MenuModuleItem instance changes. 
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */    
    configHelpText: function(p_sType, p_aArgs, p_oItem) {

        var me = this;
        var Dom = this._oDom;
        var oHelpText = p_aArgs[0];
        var oEl = this.element;
        var oConfig = this.cfg;
        var aNodes = [oEl, this._oAnchor];
        var oImg = this.submenuIndicator;


        /**
        * Adds the "hashelptext" class to the necessary nodes and refires the 
        * "selected" and "disabled" configuration events
        * @ignore
        */
        function initHelpText() {

            Dom.addClass(aNodes, "hashelptext");

            if(oConfig.getProperty("disabled")) {

                oConfig.refireEvent("disabled");

            }

            if(oConfig.getProperty("selected")) {

                oConfig.refireEvent("selected");

            }                

        }


        /**
        * Removes the "hashelptext" class and corresponding DOM element (EM)
        * @ignore
        */
        function removeHelpText() {

            Dom.removeClass(aNodes, "hashelptext");

            oEl.removeChild(me._oHelpTextEM);
            me._oHelpTextEM = null;

        }


        if(this._checkDOMNode(oHelpText)) {

            if(this._oHelpTextEM) {

                this._oHelpTextEM.parentNode.replaceChild(
                    oHelpText, 
                    this._oHelpTextEM
                );

            }
            else {

                this._oHelpTextEM = oHelpText;

                oEl.insertBefore(this._oHelpTextEM, oImg);

            }

            initHelpText();

        }
        else if(this._checkString(oHelpText)) {

            if(oHelpText.length === 0) {

                removeHelpText();

            }
            else {

                if(!this._oHelpTextEM) {

                    this._oHelpTextEM = document.createElement("em");

                    oEl.insertBefore(this._oHelpTextEM, oImg);

                }

                this._oHelpTextEM.innerHTML = oHelpText;

                initHelpText();

            }

        }
        else if(!oHelpText && this._oHelpTextEM) {

            removeHelpText();

        }

    },


    /**
    * Event handler for when the "url" configuration property of
    * a MenuModuleItem instance changes.  
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */    
    configURL: function(p_sType, p_aArgs, p_oItem) {

        var sURL = p_aArgs[0];

        if(!sURL) {

            sURL = "#";

        }

        this._oAnchor.setAttribute("href", sURL);

    },


    /**
    * Event handler for when the "emphasis" configuration property of
    * a MenuModuleItem instance changes.  
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */    
    configEmphasis: function(p_sType, p_aArgs, p_oItem) {

        var bEmphasis = p_aArgs[0];
        var oAnchor = this._oAnchor;
        var oText = this._oText;
        var oConfig = this.cfg;
        var oEM;


        if(bEmphasis && oConfig.getProperty("strongemphasis")) {

            oConfig.setProperty("strongemphasis", false);

        }


        if(oAnchor) {

            if(bEmphasis) {

                oEM = document.createElement("em");
                oEM.appendChild(oText);

                oAnchor.appendChild(oEM);

            }
            else {

                oEM = this._getFirstElement(oAnchor, "EM");

                oAnchor.removeChild(oEM);
                oAnchor.appendChild(oText);

            }

        }

    },


    /**
    * Event handler for when the "strongemphasis" configuration property of
    * a MenuModuleItem instance changes. 
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */    
    configStrongEmphasis: function(p_sType, p_aArgs, p_oItem) {

        var bStrongEmphasis = p_aArgs[0];
        var oAnchor = this._oAnchor;
        var oText = this._oText;
        var oConfig = this.cfg;
        var oStrong;

        if(bStrongEmphasis && oConfig.getProperty("emphasis")) {

            oConfig.setProperty("emphasis", false);

        }

        if(oAnchor) {

            if(bStrongEmphasis) {

                oStrong = document.createElement("strong");
                oStrong.appendChild(oText);

                oAnchor.appendChild(oStrong);

            }
            else {

                oStrong = this._getFirstElement(oAnchor, "STRONG");

                oAnchor.removeChild(oStrong);
                oAnchor.appendChild(oText);

            }

        }

    },


    /**
    * Event handler for when the "disabled" configuration property of
    * a MenuModuleItem instance changes. 
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */    
    configDisabled: function(p_sType, p_aArgs, p_oItem) {

        var bDisabled = p_aArgs[0];
        var Dom = this._oDom;
        var oAnchor = this._oAnchor;
        var aNodes = [this.element, oAnchor];
        var oEM = this._oHelpTextEM;
        var oConfig = this.cfg;
        var oImg = this.submenuIndicator;
        var sImageSrc;
        var sImageAlt;


        if(oEM) {

            aNodes[2] = oEM;

        }

        if(bDisabled) {

            if(oConfig.getProperty("selected")) {

                oConfig.setProperty("selected", false);

            }

            oAnchor.removeAttribute("href");

            Dom.addClass(aNodes, "disabled");

            sImageSrc = this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH;
            sImageAlt = this.DISABLED_SUBMENU_INDICATOR_ALT_TEXT;

        }
        else {

            oAnchor.setAttribute("href", oConfig.getProperty("url"));

            Dom.removeClass(aNodes, "disabled");

            sImageSrc = this.SUBMENU_INDICATOR_IMAGE_PATH;
            sImageAlt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;

        }


        if(oImg) {

            oImg.src = this.imageRoot + sImageSrc;
            oImg.alt = sImageAlt;

        }

    },


    /**
    * Event handler for when the "selected" configuration property of
    * a MenuModuleItem instance changes. 
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */    
    configSelected: function(p_sType, p_aArgs, p_oItem) {

        if(!this.cfg.getProperty("disabled")) {

            var Dom = this._oDom;
            var bSelected = p_aArgs[0];
            var oEM = this._oHelpTextEM;
            var aNodes = [this.element, this._oAnchor];
            var oImg = this.submenuIndicator;
            var sImageSrc;


            if(oEM) {
    
                aNodes[2] = oEM;  
    
            }
    
            if(bSelected) {
    
                Dom.addClass(aNodes, "selected");
                sImageSrc = this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH;
    
            }
            else {
    
                Dom.removeClass(aNodes, "selected");
                sImageSrc = this.SUBMENU_INDICATOR_IMAGE_PATH;
    
            }
    
            if(oImg) {
    
                oImg.src = document.images[(this.imageRoot + sImageSrc)].src;

            }

        }

    },


    /**
    * Event handler for when the "submenu" configuration property of
    * a MenuModuleItem instance changes. 
    * @param {String} p_sType The name of the event that was fired.
    * @param {Array} p_aArgs Collection of arguments sent when the 
    * event was fired.
    * @param {YAHOO.widget.MenuModuleItem} p_oItem The MenuModuleItem instance 
    * that fired the event.
    */
    configSubmenu: function(p_sType, p_aArgs, p_oItem) {

        var Dom = this._oDom;
        var oEl = this.element;
        var oSubmenu = p_aArgs[0];
        var oImg = this.submenuIndicator;
        var oConfig = this.cfg;
        var aNodes = [this.element, this._oAnchor];


        if(oSubmenu) {

            // Set the submenu's parent to this MenuModuleItem instance

            oSubmenu.parent = this;

            this._oSubmenu = oSubmenu;


            if(!oImg) { 

                var me = this;

                function preloadImage(p_sPath) {

                    var sPath = me.imageRoot + p_sPath;

                    if(!document.images[sPath]) {

                        var oImg = document.createElement("img");
                        oImg.src = sPath;
                        oImg.name = sPath;
                        oImg.id = sPath;
                        oImg.style.display = "none";
                        
                        document.body.appendChild(oImg);

                    }
                
                }

                preloadImage(this.SUBMENU_INDICATOR_IMAGE_PATH);
                preloadImage(this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH);
                preloadImage(this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH);

                oImg = document.createElement("img");
                oImg.src = (this.imageRoot + this.SUBMENU_INDICATOR_IMAGE_PATH);
                oImg.alt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;

                oEl.appendChild(oImg);

                this.submenuIndicator = oImg;

                Dom.addClass(aNodes, "hassubmenu");


                if(oConfig.getProperty("disabled")) {

                    oConfig.refireEvent("disabled");

                }

                if(oConfig.getProperty("selected")) {

                    oConfig.refireEvent("selected");

                }                

            }

        }
        else {

            Dom.removeClass(aNodes, "hassubmenu");

            if(oImg) {

                oEl.removeChild(oImg);

            }

            if(this._oSubmenu) {

                this._oSubmenu.destroy();

            }

        }

    },


    // Public methods

	/**
	* Initializes an item's configurable properties.
	*/
	initDefaultConfig : function() {

        var oConfig = this.cfg;
        var CheckBoolean = oConfig.checkBoolean;


        // Define the config properties

        oConfig.addProperty(
            "text", 
            { 
                value: "", 
                handler: this.configText, 
                validator: this._checkString, 
                suppressEvent: true 
            }
        );
        
        oConfig.addProperty("helptext", { handler: this.configHelpText });
        
        oConfig.addProperty(
            "url", 
            { value: "#", handler: this.configURL, suppressEvent: true }
        );
        
        oConfig.addProperty(
            "emphasis", 
            { 
                value: false, 
                handler: this.configEmphasis, 
                validator: CheckBoolean, 
                suppressEvent: true 
            }
        );

        oConfig.addProperty(
            "strongemphasis",
            {
                value: false,
                handler: this.configStrongEmphasis,
                validator: CheckBoolean,
                suppressEvent: true
            }
        );

        oConfig.addProperty(
            "disabled",
            {
                value: false,
                handler: this.configDisabled,
                validator: CheckBoolean,
                suppressEvent: true
            }
        );

        oConfig.addProperty(
            "selected",
            {
                value: false,
                handler: this.configSelected,
                validator: CheckBoolean,
                suppressEvent: true
            }
        );

        oConfig.addProperty("submenu", { handler: this.configSubmenu });

	},


    /**
    * Finds the next enabled MenuModuleItem instance in a MenuModule instance 
    * @return Returns a MenuModuleItem instance.
    * @type YAHOO.widget.MenuModuleItem
    */
    getNextEnabledSibling: function() {

        if(this.parent instanceof YAHOO.widget.MenuModule) {

            var nGroupIndex = this.groupIndex;

            /**
            * Returns the next item in an array 
            * @param {p_aArray} An array
            * @param {p_nStartIndex} The index to start searching the array 
            * @ignore
            * @return Returns an item in an array
            * @type Object 
            */
            function getNextArrayItem(p_aArray, p_nStartIndex) {
    
                return p_aArray[p_nStartIndex] || 
                    getNextArrayItem(p_aArray, (p_nStartIndex+1));
    
            }
    
    
            var aItemGroups = this.parent.getItemGroups();
            var oNextItem;
    
    
            if(this.index < (aItemGroups[nGroupIndex].length - 1)) {
    
                oNextItem = getNextArrayItem(
                        aItemGroups[nGroupIndex], 
                        (this.index+1)
                    );
    
            }
            else {
    
                var nNextGroupIndex;
    
                if(nGroupIndex < (aItemGroups.length - 1)) {
    
                    nNextGroupIndex = nGroupIndex + 1;
    
                }
                else {
    
                    nNextGroupIndex = 0;
    
                }
    
                var aNextGroup = getNextArrayItem(aItemGroups, nNextGroupIndex);
    
                // Retrieve the first MenuModuleItem instance in the next group
    
                oNextItem = getNextArrayItem(aNextGroup, 0);
    
            }
    
            return oNextItem.cfg.getProperty("disabled") ? 
                        oNextItem.getNextEnabledSibling() : oNextItem;

        }

    },


    /**
    * Finds the previous enabled MenuModuleItem instance in a 
    * MenuModule instance 
    * @return Returns a MenuModuleItem instance.
    * @type YAHOO.widget.MenuModuleItem
    */
    getPreviousEnabledSibling: function() {

        if(this.parent instanceof YAHOO.widget.MenuModule) {

            var nGroupIndex = this.groupIndex;

            /**
            * Returns the previous item in an array 
            * @param {p_aArray} An array
            * @param {p_nStartIndex} The index to start searching the array 
            * @ignore
            * @return Returns an item in an array
            * @type Object 
            */
            function getPreviousArrayItem(p_aArray, p_nStartIndex) {
    
                return p_aArray[p_nStartIndex] || 
                    getPreviousArrayItem(p_aArray, (p_nStartIndex-1));
    
            }


            /**
            * Get the index of the first item in an array 
            * @param {p_aArray} An array
            * @param {p_nStartIndex} The index to start searching the array 
            * @ignore
            * @return Returns an item in an array
            * @type Object 
            */    
            function getFirstItemIndex(p_aArray, p_nStartIndex) {
    
                return p_aArray[p_nStartIndex] ? 
                    p_nStartIndex : 
                    getFirstItemIndex(p_aArray, (p_nStartIndex+1));
    
            }
    
            var aItemGroups = this.parent.getItemGroups();
            var oPreviousItem;
    
            if(
                this.index > getFirstItemIndex(aItemGroups[nGroupIndex], 0)
            ) {
    
                oPreviousItem = 
                    getPreviousArrayItem(
                        aItemGroups[nGroupIndex], 
                        (this.index-1)
                    );
    
            }
            else {
    
                var nPreviousGroupIndex;
    
                if(nGroupIndex > getFirstItemIndex(aItemGroups, 0)) {
    
                    nPreviousGroupIndex = nGroupIndex - 1;
    
                }
                else {
    
                    nPreviousGroupIndex = aItemGroups.length - 1;
    
                }
    
                var aPreviousGroup = 
                        getPreviousArrayItem(aItemGroups, nPreviousGroupIndex);
    
                oPreviousItem = 
                    getPreviousArrayItem(
                        aPreviousGroup, 
                        (aPreviousGroup.length - 1)
                    );
    
            }
    
            return oPreviousItem.cfg.getProperty("disabled") ? 
                    oPreviousItem.getPreviousEnabledSibling() : oPreviousItem;

        }

    },


    /**
    * Causes a MenuModuleItem instance to receive the focus and fires the
    * focus event.
    */
    focus: function() {

        var oParent = this.parent;
        var oAnchor = this._oAnchor;
        var oActiveItem = oParent.activeItem;

        if(
            !this.cfg.getProperty("disabled") && 
            oParent && 
            oParent.cfg.getProperty("visible")
        ) {

            if(oActiveItem) {

                oActiveItem.blur();

            }

            oAnchor.focus();

            /*
                Opera 8.5 doesn't always focus the anchor if a MenuModuleItem
                instance has a submenu, this is fixed by calling "focus"
                twice.
            */
            if(oParent && this.browser == "opera" && this._oSubmenu) {

                oAnchor.focus();

            }

            this.focusEvent.fire();

        }

    },


    /**
    * Causes a MenuModuleItem instance to lose focus and fires the onblur event.
    */    
    blur: function() {

        var oParent = this.parent;

        if(
            !this.cfg.getProperty("disabled") && 
            oParent && 
            this._oDom.getStyle(oParent.element, "visibility") == "visible"
        ) {

            this._oAnchor.blur();

            this.blurEvent.fire();

        }

    },


	/**
	* Removes a MenuModuleItem instance's HTMLLIElement from it's parent
    * HTMLUListElement node.
	*/
    destroy: function() {

        var oEl = this.element;

        if(oEl) {

            // Remove CustomEvent listeners
    
            this.mouseOverEvent.unsubscribeAll();
            this.mouseOutEvent.unsubscribeAll();
            this.mouseDownEvent.unsubscribeAll();
            this.mouseUpEvent.unsubscribeAll();
            this.clickEvent.unsubscribeAll();
            this.keyPressEvent.unsubscribeAll();
            this.keyDownEvent.unsubscribeAll();
            this.keyUpEvent.unsubscribeAll();
            this.focusEvent.unsubscribeAll();
            this.blurEvent.unsubscribeAll();
            this.cfg.configChangedEvent.unsubscribeAll();


            // Remove the element from the parent node

            var oParentNode = oEl.parentNode;

            if(oParentNode) {

                oParentNode.removeChild(oEl);

                this.destroyEvent.fire();

            }

            this.destroyEvent.unsubscribeAll();

        }

    }

};


/**
* @class Extends YAHOO.widget.MenuModule to provide a set of default mouse and 
* key event behaviors.
* @constructor
* @extends YAHOO.widget.MenuModule
* @base YAHOO.widget.MenuModule
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a Menu instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.Menu = function(p_oElement, p_oConfig) {

    YAHOO.widget.Menu.superclass.constructor.call(
            this, 
            p_oElement,
            p_oConfig
        );

};

YAHOO.extend(YAHOO.widget.Menu, YAHOO.widget.MenuModule);


/**
* The Menu class's initialization method. This method is automatically 
* called by the constructor, and sets up all DOM references for pre-existing 
* markup, and creates required markup if it is not already present.
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a Menu instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.Menu.prototype.init = function(p_oElement, p_oConfig) {

    if(!this.ITEM_TYPE) {

        this.ITEM_TYPE = YAHOO.widget.MenuItem;

    }


    // Call the init of the superclass (YAHOO.widget.Menu)

    YAHOO.widget.Menu.superclass.init.call(this, p_oElement);


    this.beforeInitEvent.fire(YAHOO.widget.Menu);


    // Add event handlers

    this.showEvent.subscribe(this._onMenuShow, this, true);
    this.mouseOverEvent.subscribe(this._onMenuMouseOver, this, true);
    this.keyDownEvent.subscribe(this._onMenuKeyDown, this, true);


    if(p_oConfig) {

        this.cfg.applyConfig(p_oConfig, true);

    }
    
    this.initEvent.fire(YAHOO.widget.Menu);

};


// Private event handlers

/**
* "show" Custom Event handler for a menu.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.Menu} p_oMenu The menu that fired the event.
*/
YAHOO.widget.Menu.prototype._onMenuShow = 

    function(p_sType, p_aArgs, p_oMenu) {

        var oParent = this.parent;

        if(oParent && oParent.parent instanceof YAHOO.widget.Menu) {

            var aAlignment = oParent.parent.cfg.getProperty("submenualignment");
    
            this.cfg.setProperty(
                "submenualignment", 
                [ aAlignment[0], aAlignment[1] ]
            );
        
        }

    };


/**
* "mouseover" Custom Event handler for a Menu instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.Menu} p_oMenu The Menu instance that fired the event.
*/
YAHOO.widget.Menu.prototype._onMenuMouseOver = 

    function(p_sType, p_aArgs, p_oMenu) {
    
        /*
            If the menu is a submenu, then select the menu's parent
            MenuItem instance
        */
    
        if(this.parent) {
    
            this.parent.cfg.setProperty("selected", true);
    
        }
    
    };


/**
* "mouseover" Custom Event handler for a Menu instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.Menu} p_oMenu The Menu instance that fired the event.
*/
YAHOO.widget.Menu.prototype._onMenuKeyDown = 

    function(p_sType, p_aArgs, p_oMenu) {
    
        if(this.cfg.getProperty("position") == "dynamic") {
    
            var oDOMEvent = p_aArgs[0];
            var oParent = this.parent;
        
            if(oDOMEvent.keyCode == 27) { // Esc key
        
                this.hide();
        
                // Set focus to the parent MenuItem if one exists
        
                if(oParent) {
        
                    oParent.focus();

                    if(oParent.parent instanceof YAHOO.widget.Menu) {

                        oParent.cfg.setProperty("selected", true);
        
                    }

                    YAHOO.util.Event.preventDefault(oDOMEvent);
        
                }
            
            }
        
        }
    
    };
    

// Public event handlers

/**
* Event handler fired when the resize monitor element is resized.
*/
YAHOO.widget.Menu.prototype.onDomResize = function(e, obj) {

    if(!this._handleResize) {
    
        this._handleResize = true;
        return;
    
    }

    this.logger.log("Browser font sized changed.");

    var me = this;
    var oConfig = this.cfg;

    if(oConfig.getProperty("position") == "dynamic") {

        oConfig.setProperty("width", (this._getOffsetWidth() + "px"));
        
        if(this.parent && oConfig.getProperty("visible")) {

            function align() {

                me.align();
            
            }

            window.setTimeout(align, 0);
            
        }

    }

    YAHOO.widget.Menu.superclass.onDomResize.call(this, e, obj);

};    


/**
* @class The MenuItem class allows you to create and modify an item for a
* Menu instance.  MenuItem extends YAHOO.widget.MenuModuleItem to provide a 
* set of default mouse and key event behaviors.
* @constructor
* @extends YAHOO.widget.MenuModuleItem
* @base YAHOO.widget.MenuModuleItem
* @param {String or HTMLElement} p_oObject String or HTMLElement 
* (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
* source HTMLElement node.
* @param {Object} p_oConfig The configuration object literal containing 
* the configuration for a MenuItem instance. See the configuration 
* class documentation for more details.
*/
YAHOO.widget.MenuItem = function(p_oObject, p_oConfig) {

    YAHOO.widget.MenuItem.superclass.constructor.call(
        this, 
        p_oObject, 
        p_oConfig
    );

};

YAHOO.extend(YAHOO.widget.MenuItem, YAHOO.widget.MenuModuleItem);


/**
* The MenuItem class's initialization method. This method is automatically
* called by the constructor, and sets up all DOM references for
* pre-existing markup, and creates required markup if it is not
* already present.
* @param {String or HTMLElement} p_oObject String or HTMLElement 
* (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
* source HTMLElement node.
* @param {Object} p_oConfig The configuration object literal containing 
* the configuration for a MenuItem instance. See the configuration 
* class documentation for more details.
*/
YAHOO.widget.MenuItem.prototype.init = function(p_oObject, p_oConfig) {

    if(!this.SUBMENU_TYPE) {

        this.SUBMENU_TYPE = YAHOO.widget.Menu;

    }

    if(!this.SUBMENU_ITEM_TYPE) {

        this.SUBMENU_ITEM_TYPE = YAHOO.widget.MenuItem;

    }


    /* 
        Call the init of the superclass (YAHOO.widget.MenuModuleItem)
        Note: We don't pass the user config in here yet 
        because we only want it executed once, at the lowest 
        subclass level.
    */ 

    YAHOO.widget.MenuItem.superclass.init.call(this, p_oObject);  


    // Add event handlers to each "MenuItem" instance

    this.keyDownEvent.subscribe(this._onKeyDown, this, true);
    this.mouseOverEvent.subscribe(this._onMouseOver, this, true);
    this.mouseOutEvent.subscribe(this._onMouseOut, this, true);

    var oConfig = this.cfg;

    if(p_oConfig) {

        oConfig.applyConfig(p_oConfig, true);

    }

    oConfig.fireQueue();

};


// Constants

/**
* Constant representing the path to the image to be used for the checked state.
* @final
* @type String
*/
YAHOO.widget.MenuItem.prototype.CHECKED_IMAGE_PATH = 
    "nt/ic/ut/bsc/menuchk8_nrm_1.gif";

/**
* Constant representing the path to the image to be used for the selected 
* checked state.
* @final
* @type String
*/
YAHOO.widget.MenuItem.prototype.SELECTED_CHECKED_IMAGE_PATH = 
    "nt/ic/ut/bsc/menuchk8_hov_1.gif";

/**
* Constant representing the path to the image to be used for the disabled 
* checked state.
* @final
* @type String
*/
YAHOO.widget.MenuItem.prototype.DISABLED_CHECKED_IMAGE_PATH = 
    "nt/ic/ut/bsc/menuchk8_dim_1.gif";

/**
* Constant representing the alt text for the image to be used for the 
* checked image.
* @final
* @type String
*/
YAHOO.widget.MenuItem.prototype.CHECKED_IMAGE_ALT_TEXT = "Checked.";


/**
* Constant representing the alt text for the image to be used for the 
* checked image when the item is disabled.
* @final
* @type String
*/
YAHOO.widget.MenuItem.prototype.DISABLED_CHECKED_IMAGE_ALT_TEXT = 
    "Checked. (Item disabled.)";


// Private properties

/**
* Reference to the HTMLImageElement used to create the checked
* indicator for a MenuItem instance.
* @private
* @type {HTMLImageElement}
*/
YAHOO.widget.MenuItem.prototype._checkImage = null;


// Private event handlers


/**
* "keydown" Custom Event handler for a MenuItem instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuItem.prototype._onKeyDown = 

    function(p_sType, p_aArgs, p_oMenuItem) {

        var Event = YAHOO.util.Event;
        var oDOMEvent = p_aArgs[0];
        var oParent = this.parent;
        var oConfig = this.cfg;
        var oMenuItem;
    

        switch(oDOMEvent.keyCode) {
    
            case 38:    // Up arrow
            case 40:    // Down arrow
    
                if(
                    this == oParent.activeItem && 
                    !oConfig.getProperty("selected")
                ) {
    
                    oConfig.setProperty("selected", true);
    
                }
                else {
    
                    var oNextItem = (oDOMEvent.keyCode == 38) ? 
                            this.getPreviousEnabledSibling() : 
                            this.getNextEnabledSibling();
            
                    if(oNextItem) {

                        oParent.clearActiveItem();

                        oNextItem.cfg.setProperty("selected", true);
            
                        oNextItem.focus();
    
                    }
    
                }
    
                Event.preventDefault(oDOMEvent);

            break;
            
    
            case 39:    // Right arrow

                oParent.clearActiveItem();

                oConfig.setProperty("selected", true);
                
                this.focus();


                var oSubmenu = oConfig.getProperty("submenu");
    
                if(oSubmenu) {

                    oSubmenu.show();
                    oSubmenu.setInitialSelection();                    
    
                }
                else if(
                    YAHOO.widget.MenuBarItem && 
                    oParent.parent && 
                    oParent.parent instanceof YAHOO.widget.MenuBarItem
                ) {

                    oParent.hide();
    
                    // Set focus to the parent MenuItem if one exists
    
                    oMenuItem = oParent.parent;
    
                    if(oMenuItem) {
    
                        oMenuItem.focus();
                        oMenuItem.cfg.setProperty("selected", true);
    
                    }                    
                
                }
    
                Event.preventDefault(oDOMEvent);

            break;
    
    
            case 37:    // Left arrow
    
                // Only hide if this this is a MenuItem of a submenu
    
                if(oParent.parent) {
    
                    oParent.hide();
    
                    // Set focus to the parent MenuItem if one exists
    
                    oMenuItem = oParent.parent;
    
                    if(oMenuItem) {
    
                        oMenuItem.focus();
                        oMenuItem.cfg.setProperty("selected", true);
    
                    }
    
                }

                Event.preventDefault(oDOMEvent);
    
            break;        
    
        }
    
    };


/**
* "mouseover" Custom Event handler for a MenuItem instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuItem.prototype._onMouseOver = 

    function(p_sType, p_aArgs, p_oMenuItem) {

        var oParent = this.parent;
        var oConfig = this.cfg;
        var oActiveItem = oParent.activeItem;
    
    
        // Hide any other submenus that might be visible
    
        if(oActiveItem && oActiveItem != this) {
    
            oParent.clearActiveItem();
    
        }
    
    
        // Select and focus the current MenuItem instance
    
        oConfig.setProperty("selected", true);
        this.focus();
    
    
        // Show the submenu for this instance
    
        var oSubmenu = oConfig.getProperty("submenu");
    
        if(oSubmenu) {
    
            oSubmenu.show();
    
        }
    
    };


/**
* "mouseout" Custom Event handler for a MenuItem instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuItem.prototype._onMouseOut = 

    function(p_sType, p_aArgs, p_oMenuItem) {
    
        var oConfig = this.cfg;
        var oSubmenu = oConfig.getProperty("submenu");

        oConfig.setProperty("selected", false);
    
        if(oSubmenu) {
    
            var oDOMEvent = p_aArgs[0];
            var oRelatedTarget = YAHOO.util.Event.getRelatedTarget(oDOMEvent);
    
            if(
                !(
                    oRelatedTarget == oSubmenu.element || 
                    YAHOO.util.Dom.isAncestor(oSubmenu.element, oRelatedTarget)
                )
            ) {
    
                oSubmenu.hide();
    
            }
    
        }
    
    };


// Event handlers for configuration properties

/**
* Event handler for when the "checked" configuration property of
* a MenuItem instance changes. 
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the 
* event was fired.
* @param {YAHOO.widget.MenuItem} p_oItem The MenuItem instance 
* that fired the event.
*/    
YAHOO.widget.MenuItem.prototype.configChecked =

    function(p_sType, p_aArgs, p_oItem) {
    
        var Dom = YAHOO.util.Dom;
        var bChecked = p_aArgs[0];
        var oEl = this.element;
        var oConfig = this.cfg;
        var oImg;
        

        if(bChecked) {

            var me = this;

            function preloadImage(p_sPath) {

                var sPath = me.imageRoot + p_sPath;

                if(!document.images[sPath]) {

                    var oImg = document.createElement("img");
                    oImg.src = sPath;
                    oImg.name = sPath;
                    oImg.id = sPath;
                    oImg.style.display = "none";
                    
                    document.body.appendChild(oImg);

                }
            
            }

            preloadImage(this.CHECKED_IMAGE_PATH);
            preloadImage(this.SELECTED_CHECKED_IMAGE_PATH);
            preloadImage(this.DISABLED_CHECKED_IMAGE_PATH);


            oImg = document.createElement("img");
            oImg.src = (this.imageRoot + this.CHECKED_IMAGE_PATH);
            oImg.alt = this.CHECKED_IMAGE_ALT_TEXT;

            var oSubmenu = this.cfg.getProperty("submenu");

            if(oSubmenu) {

                oEl.insertBefore(oImg, oSubmenu.element);

            }
            else {

                oEl.appendChild(oImg);            

            }


            Dom.addClass([oEl, oImg], "checked");

            this._checkImage = oImg;

            if(oConfig.getProperty("disabled")) {

                oConfig.refireEvent("disabled");

            }

            if(oConfig.getProperty("selected")) {

                oConfig.refireEvent("selected");

            }
        
        }
        else {

            oImg = this._checkImage;

            Dom.removeClass([oEl, oImg], "checked");

            if(oImg) {

                oEl.removeChild(oImg);

            }

            this._checkImage = null;
        
        }

    };
    

/**
* Event handler for when the "selected" configuration property of
* a MenuItem instance changes. 
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the 
* event was fired.
* @param {YAHOO.widget.MenuItem} p_oItem The MenuItem instance 
* that fired the event.
*/    
YAHOO.widget.MenuItem.prototype.configSelected = 

    function(p_sType, p_aArgs, p_oItem) {

        YAHOO.widget.MenuItem.superclass.configSelected.call(
                this, p_sType, p_aArgs, p_oItem
            );        
    
        var oConfig = this.cfg;

        if(!oConfig.getProperty("disabled") && oConfig.getProperty("checked")) {

            var bSelected = p_aArgs[0];

            var sSrc = this.imageRoot + (bSelected ? 
                this.SELECTED_CHECKED_IMAGE_PATH : this.CHECKED_IMAGE_PATH);

            this._checkImage.src = document.images[sSrc].src;
            
        }            
    
    };


/**
* Event handler for when the "disabled" configuration property of
* a MenuItem instance changes. 
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the 
* event was fired.
* @param {YAHOO.widget.MenuItem} p_oItem The MenuItem instance 
* that fired the event.
*/    
YAHOO.widget.MenuItem.prototype.configDisabled = 

    function(p_sType, p_aArgs, p_oItem) {
    
        YAHOO.widget.MenuItem.superclass.configDisabled.call(
                this, p_sType, p_aArgs, p_oItem
            );        
    
        if(this.cfg.getProperty("checked")) {
    
            var bDisabled = p_aArgs[0];
            var sAlt = this.CHECKED_IMAGE_ALT_TEXT;
            var sSrc = this.CHECKED_IMAGE_PATH;
            var oImg = this._checkImage;
            
            if(bDisabled) {
    
                sAlt = this.DISABLED_CHECKED_IMAGE_ALT_TEXT;
                sSrc = this.DISABLED_CHECKED_IMAGE_PATH;
            
            }

            oImg.src = document.images[(this.imageRoot + sSrc)].src;
            oImg.alt = sAlt;
            
        }    
            
    };


// Public methods
    
/**
* Initializes the class's configurable properties which can be changed using 
* the MenuModule's Config object (cfg).
*/
YAHOO.widget.MenuItem.prototype.initDefaultConfig = function() {

    YAHOO.widget.MenuItem.superclass.initDefaultConfig.call(this);

	// Add configuration properties

    this.cfg.addProperty(
        "checked", 
        {
            value: false, 
            handler: this.configChecked, 
            validator: this.cfg.checkBoolean, 
            suppressEvent: true 
        } 
    );

};


/**
* @class Creates a list of options which vary depending on the context in 
* which the menu is invoked.
* @constructor
* @extends YAHOO.widget.Menu
* @base YAHOO.widget.Menu
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a ContextMenu instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) {

    YAHOO.widget.ContextMenu.superclass.constructor.call(
            this, 
            p_oElement,
            p_oConfig
        );

};

YAHOO.extend(YAHOO.widget.ContextMenu, YAHOO.widget.Menu);


YAHOO.widget.ContextMenu.prototype._oTrigger = null;


/**
* The ContextMenu class's initialization method. This method is automatically  
* called by the constructor, and sets up all DOM references for pre-existing 
* markup, and creates required markup if it is not already present.
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a ContextMenu instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.ContextMenu.prototype.init = function(p_oElement, p_oConfig) {

    if(!this.ITEM_TYPE) {

        this.ITEM_TYPE = YAHOO.widget.ContextMenuItem;

    }


    // Call the init of the superclass (YAHOO.widget.Menu)

    YAHOO.widget.ContextMenu.superclass.init.call(this, p_oElement);


    this.beforeInitEvent.fire(YAHOO.widget.ContextMenu);


    if(p_oConfig) {

        this.cfg.applyConfig(p_oConfig, true);

    }
    
    
    this.initEvent.fire(YAHOO.widget.ContextMenu);


};


// Private event handlers

/**
* "mousedown" event handler for the document object.
* @private
* @param {Event} p_oEvent Event object passed back by the 
* event utility (YAHOO.util.Event).
* @param {YAHOO.widget.ContextMenu} p_oMenu The ContextMenu instance 
* handling the event.
*/
YAHOO.widget.ContextMenu.prototype._onDocumentMouseDown = 

    function(p_oEvent, p_oMenu) {
    
        var oTarget = YAHOO.util.Event.getTarget(p_oEvent);
        var oTargetEl = this._oTargetElement;
    
        if(
            oTarget != oTargetEl || 
            !YAHOO.util.Dom.isAncestor(oTargetEl, oTarget)
        ) {
    
            this.hide();
        
        }
    
    };


/**
* "click" event handler for the HTMLElement node that triggered the event. 
* Used to cancel default behaviors in Opera.
* @private
* @param {Event} p_oEvent Event object passed back by the 
* event utility (YAHOO.util.Event).
* @param {YAHOO.widget.ContextMenu} p_oMenu The ContextMenu instance 
* handling the event.
*/
YAHOO.widget.ContextMenu.prototype._onTriggerClick = 

    function(p_oEvent, p_oMenu) {

        if(p_oEvent.ctrlKey) {
        
            YAHOO.util.Event.stopEvent(p_oEvent);
    
        }
        
    };


/**
* "contextmenu" event handler ("mousedown" for Opera) for the HTMLElement 
* node that triggered the event.
* @private
* @param {Event} p_oEvent Event object passed back by the 
* event utility (YAHOO.util.Event).
* @param {YAHOO.widget.ContextMenu} p_oMenu The ContextMenu instance 
* handling the event.
*/
YAHOO.widget.ContextMenu.prototype._onTriggerContextMenu = 

    function(p_oEvent, p_oMenu) {

        var Event = YAHOO.util.Event;
        var oConfig = this.cfg;

        if(p_oEvent.type == "mousedown") {
        
            if(!p_oEvent.ctrlKey) {
    
                return;
            
            }
        
            Event.stopEvent(p_oEvent);
    
        }
    
    
        this.contextEventTarget = Event.getTarget(p_oEvent);
    
    
        // Position and display the context menu
    
        var nX = Event.getPageX(p_oEvent);
        var nY = Event.getPageY(p_oEvent);
    
    
        oConfig.applyConfig( { x:nX, y:nY, visible:true } );
        oConfig.fireQueue();
    
    
        // Prevent the browser's default context menu from appearing
    
        Event.preventDefault(p_oEvent);
    
    };


// Public properties

/**
* Returns the HTMLElement node that was the target of the "contextmenu" 
* DOM event.
* @type HTMLElement
*/
YAHOO.widget.ContextMenu.prototype.contextEventTarget = null;


// Public methods

/**
* Initializes the class's configurable properties which can be changed using 
* a ContextMenu instance's Config object (cfg).
*/
YAHOO.widget.ContextMenu.prototype.initDefaultConfig = function() {

    YAHOO.widget.ContextMenu.superclass.initDefaultConfig.call(this);


	// Add a configuration property

    this.cfg.addProperty("trigger", { handler: this.configTrigger });

};


// Event handlers for configuration properties

/**
* Event handler for when the "trigger" configuration property of
* a MenuItem instance. 
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the 
* event was fired.
* @param {YAHOO.widget.ContextMenu} p_oMenu The ContextMenu that instance fired
* the event.
*/
YAHOO.widget.ContextMenu.prototype.configTrigger = 

    function(p_sType, p_aArgs, p_oMenu) {
    
        var Event = YAHOO.util.Event;
        var oTrigger = p_aArgs[0];
    
        if(oTrigger) {
    

            /*
                If there is a current "trigger" - remove the event handlers 
                from that element(s) before assigning new ones
            */
            if(this._oTrigger) {
            
                Event.purgeElement(this._oTrigger);

            }


            this._oTrigger = oTrigger;


            /*
                Listen for the "mousedown" event in Opera b/c it does not 
                support the "contextmenu" event
            */ 
      
            var bOpera = (this.browser == "opera");
    
            Event.addListener(
                oTrigger, 
                (bOpera ? "mousedown" : "contextmenu"), 
                this._onTriggerContextMenu,
                this,
                true
            );
    
    
            /*
                Assign a "click" event handler to the trigger element(s) for
                Opera to prevent default browser behaviors.
            */
    
            if(bOpera) {
            
                Event.addListener(
                    oTrigger, 
                    "click", 
                    this._onTriggerClick,
                    this,
                    true
                );
    
            }
    
    
            // Assign a "mousedown" event handler to the document
        
            Event.addListener(
                document, 
                "mousedown", 
                this._onDocumentMouseDown,
                this,
                true
            );        
    
        }
        
    };


/**
* @class Creates an item for a context menu instance.
* @constructor
* @extends YAHOO.widget.MenuItem
* @base YAHOO.widget.MenuItem
* @param {String or HTMLElement} p_oObject String or HTMLElement 
* (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
* source HTMLElement node.
* @param {Object} p_oConfig The configuration object literal containing 
* the configuration for a ContextMenuItem instance. See the configuration 
* class documentation for more details.
*/
YAHOO.widget.ContextMenuItem = function(p_oObject, p_oConfig) {

    YAHOO.widget.ContextMenuItem.superclass.constructor.call(
        this, 
        p_oObject, 
        p_oConfig
    );

};

YAHOO.extend(YAHOO.widget.ContextMenuItem, YAHOO.widget.MenuItem);


/**
* The ContextMenuItem class's initialization method. This method is
* automatically called by the constructor, and sets up all DOM references for
* pre-existing markup, and creates required markup if it is not
* already present.
* @param {String or HTMLElement} p_oObject String or HTMLElement 
* (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
* source HTMLElement node.
* @param {Object} p_oConfig The configuration object literal containing 
* the configuration for a ContextMenuItem instance. See the configuration 
* class documentation for more details.
*/
YAHOO.widget.ContextMenuItem.prototype.init = 

    function(p_oObject, p_oConfig) {
    
        if(!this.SUBMENU_TYPE) {
    
            this.SUBMENU_TYPE = YAHOO.widget.ContextMenu;
    
        }
    
        if(!this.SUBMENU_ITEM_TYPE) {
    
            this.SUBMENU_ITEM_TYPE = YAHOO.widget.ContextMenuItem;
    
        }
    
    
        /* 
            Call the init of the superclass (YAHOO.widget.MenuItem)
            Note: We don't pass the user config in here yet 
            because we only want it executed once, at the lowest 
            subclass level.
        */ 
    
        YAHOO.widget.ContextMenuItem.superclass.init.call(this, p_oObject);

        var oConfig = this.cfg;
    
        if(p_oConfig) {
    
            oConfig.applyConfig(p_oConfig, true);
    
        }
    
        oConfig.fireQueue();
    
    };


/**
* @class Horizontal collection of items, each of which can contain a submenu.
* Extends YAHOO.widget.MenuModule to provide a set of default mouse and 
* key event behaviors.
* @constructor
* @extends YAHOO.widget.MenuModule
* @base YAHOO.widget.MenuModule
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a MenuBar instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) {

    YAHOO.widget.MenuBar.superclass.constructor.call(
            this, 
            p_oElement,
            p_oConfig
        );

};

YAHOO.extend(YAHOO.widget.MenuBar, YAHOO.widget.MenuModule);


/**
* The MenuBar class's initialization method. This method is automatically 
* called by the constructor, and sets up all DOM references for pre-existing 
* markup, and creates required markup if it is not already present.
* @param {String or HTMLElement} p_oElement String id or HTMLElement 
* (either HTMLSelectElement or HTMLDivElement) of the source HTMLElement node.
* @param {Object} p_oConfig Optional. The configuration object literal 
* containing the configuration for a MenuBar instance. See 
* configuration class documentation for more details.
*/
YAHOO.widget.MenuBar.prototype.init = function(p_oElement, p_oConfig) {

    if(!this.ITEM_TYPE) {

        this.ITEM_TYPE = YAHOO.widget.MenuBarItem;

    }


    // Call the init of the superclass (YAHOO.widget.MenuModule)

    YAHOO.widget.MenuBar.superclass.init.call(this, p_oElement);


    this.beforeInitEvent.fire(YAHOO.widget.MenuBar);

    var oConfig = this.cfg;

    /*
        Set the default value for the "position" configuration property
        to "static" 
    */
    if(!p_oConfig || (p_oConfig && !p_oConfig.position)) {

        oConfig.queueProperty("position", "static");

    }

    /*
        Set the default value for the "submenualignment" configuration property
        to "tl" and "bl" 
    */
    if(!p_oConfig || (p_oConfig && !p_oConfig.submenualignment)) {

        oConfig.queueProperty("submenualignment", ["tl","bl"]);

    }


    if(p_oConfig) {

        oConfig.applyConfig(p_oConfig, true);

    }
    
    this.initEvent.fire(YAHOO.widget.MenuBar);

};


// Constants

/**
* Constant representing the CSS class(es) to be applied to the root 
* HTMLDivElement of the MenuBar instance.
* @final
* @type String
*/
YAHOO.widget.MenuBar.prototype.CSS_CLASS_NAME = "yuimenubar";


/**
* @class The MenuBarItem class allows you to create and modify an item for a
* MenuBar instance.  MenuBarItem extends YAHOO.widget.MenuModuleItem to provide 
* a set of default mouse and key event behaviors.
* @constructor
* @extends YAHOO.widget.MenuModuleItem
* @base YAHOO.widget.MenuModuleItem
* @param {String or HTMLElement} p_oObject String or HTMLElement 
* (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
* source HTMLElement node.
* @param {Object} p_oConfig The configuration object literal containing 
* the configuration for a MenuBarItem instance. See the configuration 
* class documentation for more details.
*/
YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) {

    YAHOO.widget.MenuBarItem.superclass.constructor.call(
        this, 
        p_oObject, 
        p_oConfig
    );

};

YAHOO.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuModuleItem);


/**
* The MenuBarItem class's initialization method. This method is automatically
* called by the constructor, and sets up all DOM references for
* pre-existing markup, and creates required markup if it is not
* already present.
* @param {String or HTMLElement} p_oObject String or HTMLElement 
* (either HTMLLIElement, HTMLOptGroupElement or HTMLOptionElement) of the 
* source HTMLElement node.
* @param {Object} p_oConfig The configuration object literal containing 
* the configuration for a MenuBarItem instance. See the configuration 
* class documentation for more details.
*/
YAHOO.widget.MenuBarItem.prototype.init = function(p_oObject, p_oConfig) {

    if(!this.SUBMENU_TYPE) {

        this.SUBMENU_TYPE = YAHOO.widget.Menu;

    }

    if(!this.SUBMENU_ITEM_TYPE) {

        this.SUBMENU_ITEM_TYPE = YAHOO.widget.MenuItem;

    }


    /* 
        Call the init of the superclass (YAHOO.widget.MenuModuleItem)
        Note: We don't pass the user config in here yet 
        because we only want it executed once, at the lowest 
        subclass level.
    */ 

    YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject);  


    // Add event handlers to each "MenuBarItem" instance

    this.keyDownEvent.subscribe(this._onKeyDown, this, true);

    var oConfig = this.cfg;

    if(p_oConfig) {

        oConfig.applyConfig(p_oConfig, true);

    }

    oConfig.fireQueue();

};


// Constants

/**
* Constant representing the CSS class(es) to be applied to the root 
* HTMLLIElement of the MenuBarItem.
* @final
* @type String
*/
YAHOO.widget.MenuBarItem.prototype.CSS_CLASS_NAME = "yuimenubaritem";


/**
* Constant representing the path to the image to be used for the submenu
* arrow indicator.
* @final
* @type String
*/
YAHOO.widget.MenuBarItem.prototype.SUBMENU_INDICATOR_IMAGE_PATH =
    "nt/ic/ut/alt1/menuarodwn8_nrm_1.gif";


/**
* Constant representing the path to the image to be used for the submenu
* arrow indicator when a MenuBarItem instance is selected.
* @final
* @type String
*/
YAHOO.widget.MenuBarItem.prototype.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH =
    "nt/ic/ut/alt1/menuarodwn8_hov_1.gif";


/**
* Constant representing the path to the image to be used for the submenu
* arrow indicator when a MenuBarItem instance is disabled.
* @final
* @type String
*/
YAHOO.widget.MenuBarItem.prototype.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH = 
    "nt/ic/ut/alt1/menuarodwn8_dim_1.gif";


// Private event handlers

/**
* "keydown" Custom Event handler for a MenuBarItem instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.MenuModule} p_oMenuModule The MenuModule instance that 
* fired the event.
*/
YAHOO.widget.MenuBarItem.prototype._onKeyDown =

    function(p_sType, p_aArgs, p_oMenuItem) {

        var Event = YAHOO.util.Event;
        var oDOMEvent = p_aArgs[0];
        var oConfig = this.cfg;
        var oParent = this.parent;
    
        switch(oDOMEvent.keyCode) {
    
            case 37:    // Left arrow
            case 39:    // Right arrow
    
                if(
                    this == oParent.activeItem && 
                    !oConfig.getProperty("selected")
                ) {
    
                    oConfig.setProperty("selected", true);
    
                }
                else {
    
                    var oNextItem = (oDOMEvent.keyCode == 37) ? 
                            this.getPreviousEnabledSibling() : 
                            this.getNextEnabledSibling();
            
                    if(oNextItem) {

                        oParent.clearActiveItem();

                        oNextItem.cfg.setProperty("selected", true);
            
                        oNextItem.focus();
    
                    }
    
                }

                Event.preventDefault(oDOMEvent);
    
            break;
    
            case 40:    // Down arrow

                oParent.clearActiveItem();
                        
                oConfig.setProperty("selected", true);
                
                this.focus();

                var oSubmenu = oConfig.getProperty("submenu");
    
                if(oSubmenu) {
        
                    oSubmenu.show();
                    oSubmenu.setInitialSelection();
    
                }

                Event.preventDefault(oDOMEvent);
    
            break;
    
        }
    
    };